elkiwy
elkiwy

Reputation: 59

How to bind functions in let evaluating them later

I'm learning Clojure to use it with Quil to make generative art and I would like to try to understand a bit more about let function and reader macro probably.

I tried reading about reader macros from the docs but I couldn't find a clear answer to it.

I currently have this piece of code:

(let [dabs (take 10 (repeatedly #(make-watercolor 
                                   (mkpt 
                                     (randomNormal 0.1 0.9) 
                                     (randomNormal 0.1 0.9))
                                   (w (randomNormal 0.4 0.7)))))]
    (draw-multiple-watercolor dabs 3))

Which is pretty ugly and not really readable. I would like to slim down the repeated function splitting it into smaller pieces, but since those pieces will be evaluated multiple times and has random inside them I cannot store their result inside a variable and use that, instead I need to evaluate those when needed.

My question is: is there a way to do something like this

(let [randCoord (randomNormal 0.1 0.9) ;This..
      randPoint (mkpt randCoord randCoord) ;..and this doesn't should be evaluated here, but on the repeatedly function calls
      dabs (take 10 (repeatedly #(make-watercolor 
                                   randPoint ;Evaluation here
                                   (w (randomNormal 0.4 0.7)))))]
    (draw-multiple-watercolor dabs 3))

Upvotes: 1

Views: 92

Answers (1)

Taylor Wood
Taylor Wood

Reputation: 16194

One option is to use the same approach you've used with the function you pass to repeatedly: just wrap the expressions in functions that take no arguments (or only the args you want to change), then they'll be evaluated each time you invoke the function.

(let [rand-coord (fn [] (randomNormal 0.1 0.9)) ;; or #(randomNormal 0.1 0.9)
      make-dab #(make-watercolor (mkpt (rand-coord) (rand-coord))
                                 (w (randomNormal 0.4 0.7)))
      dabs (take 10 (repeatedly make-dab))]
  (draw-multiple-watercolor dabs 3))

Also take a look at letfn for defining functions in a non-top-level/namespace scope.

You might also find threading macros like ->, ->>, as->, some->, cond->, etc. can make some code more readable. For example, if you changed draw-multiple-watercolor to take its sequence of dabs last (which is fairly common practice for functions that operate on sequences in Clojure) then you could do something like this:

(let [rand-coord (fn [] (randomNormal 0.1 0.9)) ;; or #(randomNormal 0.1 0.9)
      make-dab #(make-watercolor (mkpt (rand-coord) (rand-coord))
                                 (w (randomNormal 0.4 0.7)))]
  (->> make-dab
       (repeatedly 10) ;; note: repeatedly can take a # of iterations
       (draw-multiple-watercolor 3)))

Upvotes: 3

Related Questions