How do you refer to a previous value of a var, within a loop/recur?

193 views
Skip to first unread message

Dan Campbell

unread,
Nov 23, 2014, 11:25:21 AM11/23/14
to clo...@googlegroups.com
Hi,

I have to loop through a string, consecutively replacing substrings with single characters, one substring at a time.  Each time through the loop, I need to use a cond to check that the length of the last replaced string, is shorter than the length of the previous replaced string.

So, as an artificial example, say you have a string "1001110011011", and a map or vector of substrings and replacements

( def map_translations 
{
  { "00" "0" },
  { "01" "0" },
  { "10" "0" },
  { "11" "1" }
}

( The resemblance to a conjunction truth table is coincidental.  This is the simplest example I could think of. )


So I want to loop through the map of translations several times, each time trying to simplify the original expression further, until a result of either "-1" or a single character is returned.

If the length of the replaced string is greater than or equal to the length of the last replacement, then I'd want to return a "-1" string.

But I can't figure out, how to compare the length of a result of a recur assignment, to the length of the PREVIOUS recur assignment.


So, it might look something like this (untested):



( defn simplify-string
  [ s_original-string s_substring s_replacement-character ]
  (
    let [ s_simplified-string ( clojure.string/replace s_original-string s_substring s_replacement-character )  ]
    s_simplified-string
  )
)


( defn simplify-dat-guy
  "Loops through a map of substring to character translations,
    attempting to simplify a given expression.
    e.g., '00101' through successive translations would become a shorter form
  "
  [ s_exp ]
  ( loop 
    [ [k v] map_longform-shortform-translation s_simplified-expression s_exp ] 
        ( recur [ k v ] ( simplify-string s_simplified-expression k v ) )
  )
)


( defn completely-simplify-dat-guy
  "Loops through simplify-dat-guy several times,
    until either a single character or a "-1" is returned.
    e.g., '00101' through successive simplify-dat-guys would eventually become "0".
  "
  [ s_exp ]
  ( loop 
    [ s_simplified-expression s_exp ] 
    ( 
      cond
      ( = ( .length s_simplified-expression ) 1 )
        s_simplified-expression
( comment
  I want to put a second condition in here, something like
        ( = ( .length s_simplified-expression ) ( .length PREVIOUS_s_simplified_expression ) )
        "-1"
)
        ( recur ( simplify-dat-guy s_simplified-expression ) )
    )
  )
)

James Reeves

unread,
Nov 23, 2014, 12:45:16 PM11/23/14
to clo...@googlegroups.com
Well, first of all, it would really help if you formatted your code, because currently your code is almost impossible to read.

Let's format it first:

    (defn simplify-string [original substring replacement]
      (let [simplified (str/replace original substring replacement)]
        simplified))

    (defn simplify-dat-guy [exp]
      (loop [[k v] translations exp exp] 
        (recur [k v] (simplify-string exp k v))))

    (defn completely-simplify-dat-guy [exp]
      (loop [exp exp] 
        (cond
          (= (.length exp) 1) exp
          (second-condition exp)  (recur (simplify-dat-guy exp)))))

We can simplify this a little further still by removing simplify-string, since it's just a wrapper around str/replace:

    (defn simplify-dat-guy [exp]
      (loop [[k v] translations exp exp] 
        (recur [k v] (str/replace exp k v))))

And since recur works with functions, we can also remove the outer loop:

    (defn completely-simplify-dat-guy [exp]
      (cond
        (= (.length exp) 1) exp
        (second-condition exp)  (recur (simplify-dat-guy exp)))))

The loop in "simplify-dat-guy" is also an example of a fold over a map, so we can replace it with reduce-kv:

    (defn simplify-dat-guy [exp]
      (reduce-kv str/replace exp translations))

Now let's look at your problem. Assuming I've understood correctly, you want to simplify until you have a string of length 1, or a string that cannot be reduced any further?

So in that case, something like:

    (defn simplify [expr translations]
      (if (= (count expr) 1)
        expr
        (let [next-expr (reduce-kv str/replace expr translations)]
          (if (< (count next-expr) (count expr)
            (recur next-expr)))))

If we cannot reduce the expression, rather than returning "-1", let's just return nil instead.

- James


--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dan Campbell

unread,
Nov 23, 2014, 3:05:02 PM11/23/14
to clo...@googlegroups.com, ja...@booleanknot.com

Yep, sure enough, works like a gem, thanks.  I had originally tried reduce, but wasn't able to get it to work, in this context.  reduce-kv would have saved a lot of time, on previous code.


Sorry about the formatting, James.  I structure my code differently than the standard Clojure format, and some of the tab sizes (2 or 3 spaces) got lost in the copying & pasting.  Here's my final version, which works for several input strings:

; Credit to James Reeves
(defn simplify 
    "Replace expression with simplified expressions, using
      { s_substring, s_replace-character } map of replacements
    "
    [ s_expr map_translations ]
    (if (= (count s_expr) 1)
      s_expr
    (
      let 
        [ next-expr ( reduce-kv clojure.string/replace s_expr map_translations )]
        (if ( < (count next-expr) )
          (count s_expr) 
        )
        (recur next-expr map_translations )
    )  ; else let
    )  ; if (= (count s_expr) 1)
)

James Reeves

unread,
Nov 23, 2014, 3:46:32 PM11/23/14
to Dan Campbell, clo...@googlegroups.com
I'd strongly suggest following standard Clojure formatting, otherwise you're going to run into problems. If you use your own custom formatting that only you can read easily, then people are going to find it very difficult to help or collaborate with you. Deliberately putting up barriers to communication is a strong incentive for people to pass over your posts - why should they have to reformat your code just to understand it?

- James

Sean Corfield

unread,
Nov 23, 2014, 5:27:46 PM11/23/14
to clo...@googlegroups.com
Dan, I’m with James here. Your code as presented is really hard to read for folks used to the "standard Clojure style" - the strange layout of parentheses is very distracting. The use of underscore instead of hyphen is also a bit jarring.

I’m guessing your background is C/C++/Java and you think the standard Clojure style of formatting looks weird? You’ll get used to it. Especially if you set your editor up to auto-close / auto-match parentheses for you and use some sort of "rainbow parentheses" setting (so matching parens are the same color, and each matched pair is different to its neighbor).


(defn simplify 
  "Replace expression with simplified expressions, using
  {s-substring s-replace-character} map of replacements"
  [s-expr map-translations]
  (if (= (count s-expr) 1)
    s-expr
    (let [next-expr (reduce-kv clojure.string/replace s-expr map-translations)]
      (if (< (count next-expr))
        (count s-expr))
      (recur next-expr map-translations))))

Here's how that looks in my editor:


I think your < test is incorrect (now that I can clearly see the code): (< (count next-expr)) is always true and then you’re throwing away the value of the `if` (either (count s-expr) or nil since you have no then-expression). Did you mean this, perhaps?

      (if (< (count s-expr) (count next-expr))
        s-expr
        (recur next-expr map-translations))

Sean

Dan Campbell

unread,
Nov 23, 2014, 5:42:01 PM11/23/14
to clo...@googlegroups.com

Sean,

Sorry man, but I'm not willing to change my personal coding style.  It's not a matter of getting used to Clojure.  I'm not an expert on it yet, but I've been studying and working with it, for a little over a year.  I prefer the indentation; it makes more sense to me.

Your image, though, doesn't reflect my intended indentation style for lisp code.  It looks like some reformatting took place.


As I agreed with James, if asking for assistance, I'll reformat the code first, to match the standard Clojure style.  Similarly, if I'm able to help someone else, I'll do so, in a style they are comfortable with.  For my own personal coding, which isn't completely settled yet, - gotta go with my own comfort level.

If there are other styles out there, that you know about, I'm willing to take a look at them.  Although the traditional lisp style with multiple adjacent parentheses doesn't work for me, there might be other styles worth looking at.

Perhaps the upcoming Cursive, when it hits 1.x, will have some kind of custom beautifier, which will make these points moot.


Thanks for pointing out the error in the if statement.  You're correct.



--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/GFOXhZKooyI/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Sean Corfield

unread,
Nov 23, 2014, 5:49:12 PM11/23/14
to clo...@googlegroups.com
On Nov 23, 2014, at 2:41 PM, Dan Campbell <dcwh...@gmail.com> wrote:
Sorry man, but I'm not willing to change my personal coding style.  It's not a matter of getting used to Clojure.  I'm not an expert on it yet, but I've been studying and working with it, for a little over a year.  I prefer the indentation; it makes more sense to me.

As long as you’re prepared to be told this every time you show your code to people, that’s up to you.

Your image, though, doesn't reflect my intended indentation style for lisp code.  It looks like some reformatting took place.

Of course - I reformatted it to match the Clojure community style. The image was to show rainbow parentheses, specifically.

If there are other styles out there, that you know about, I'm willing to take a look at them.  Although the traditional lisp style with multiple adjacent parentheses doesn't work for me, there might be other styles worth looking at.

All the style guides will follow that style - that’s how Lisp is written :)

Here’s the most "standard" Clojure style guide, based on the style in Clojure books, and Clojure/core’s own library style guide:


Thanks for pointing out the error in the if statement.  You're correct.

Glad I was able to help.

Sean Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)



Dan Campbell

unread,
Nov 23, 2014, 6:01:58 PM11/23/14
to clo...@googlegroups.com
"As long as you’re prepared to be told this every time you show your code to people, that’s up to you."

I think you misunderstood the response.



Plínio Balduino

unread,
Nov 24, 2014, 9:11:28 AM11/24/14
to Clojure Official
Hi Dan

Please accept my two cents.

"I prefer the indentation; it makes more sense to me."

No, he didn't misunderstand, sorry.

Anyway, Batsov's Style Guide is an awesome resource, even though you won't follow the items ipsis literis, is good to understand how to work and think in the 'Clojure Way'.

For example, when one is new to Ruby, it's normal to find the for statement there. Once one gets used to 'Ruby Style', the each method becomes the standard.

An example that I used with my team some days ago:

In Portuguese (our native language) we use nouns before adjective. "Um dia quente".

In English we use nouns after adjective. "A hot day".

Even if you swap the order in Portuguese (um quente dia) or in English (a day hot), a native speaker will understand with more or less effort. Syntactically it's not wrong, semantically is OK, but the construction it's not in the 'Portuguese way' or in the 'English way'.

So unless you don't mind to speak with yourself all the time, please try to understand and follow the rules and the way of your new environment. After all you don't want to be seen as an arrogant guy or as someone who doesn't want to learn just because your way makes more sense to you.

Please don't understand I'm judging you. I have a very bad impression of LISPs communities in general, but the Clojure community is highly newcomer-friendly and the other guys who tried to help you, for example, are high skilled and very busy professionals. In any other community (I'm cofounder of JS, Clojure and Ruby communities) you woudn't find that good will to help, unfortunately.

Sorry for the completely off-topic, best regards and welcome to community.

Plínio



You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

Dan Campbell

unread,
Nov 24, 2014, 9:38:10 AM11/24/14
to clo...@googlegroups.com
Thanks for the input, Plinio.  

If the community jumps to the conclusion that I'm arrogant, because I like to work in style that's clear & works for me, then I won't be surprised if they don't offer assistance in the future.  

From my point of view, as long as they receive my contributions or requests for assistance in a form that's palatable to them, it's not anyone's business how I code on a daily basis.

Gary Verhaegen

unread,
Nov 24, 2014, 10:58:10 AM11/24/14
to clo...@googlegroups.com
Yeah, I think the "I'll transliterate everything before anyone else sees it" part of your message was maybe not clear enough. Of course we don't care what you do when you're working alone and just for yourself.

Dan Campbell

unread,
Nov 24, 2014, 1:05:22 PM11/24/14
to clo...@googlegroups.com

Yep, I think that's what happened.  Sorry for the confusion, nuff said, back to Clojure.

Reply all
Reply to author
Forward
0 new messages