Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't my code printing like it should?

I'm trying to create a for in Clojure.

I'm following the cheats sheet from the Clojure site.

e.g:

(take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))

I'm trying to create my own for which should print "Hello World" 100 times.

(take 100 (for [a (range 100) 
      :while (< a 100)] 
    (println "Hello World")
    )
)

For some reason it's not printing Hello World 100 times. Why not?

like image 684
andre Avatar asked Sep 07 '11 22:09

andre


1 Answers

The most important thing you need to be aware of, is that sequences in Clojure are lazy. That means the items in the sequence are only evaluated when they are needed. This allows you to work with infinite sequences.

Most of the time this is what you want, but it can be confusing when you are not really interested in the values of the sequence items, but in the side-effects of the functions that create the sequence items. In your example, the sequence is made up of 100 return values of the println function, that is 100 times nil - not very interesting. But the println function has the side-effect of printing "Hello World" to stdout.

The problem is, if you never do anything with the items in the sequence, the println functions are never evaluated and the strings are not printed. It kinda works in the REPL, because the P in REPL stands for print - the return value of the expression you typed in is printed. To print the whole sequence, it must be evaluated, so you see a bunch of "Hello World"s, but also a bunch of nils. If you run the code outside of the REPL (without using the returned value), you won't see the Hello Worlds.

If you're interested in the side-effects of your item-generating functions, you can use doall or doseq. doall forces the whole sequence to be evaluated and returns it:

(doall (for [a (range 100)] (println "Hello World")))

doseq doesn't return the sequence (it returns nil) and has a syntax like for:

(doseq [a (range 100)] (println "Hello World"))

Also note that you really only need to supply the desired count (100) once. The range function already produces a sequence with 100 items in it. The :while clause in your code is redundant, as is the take function (taking the first 100 items from a sequence with 100 items doesn't do much).

Hope this helps!

like image 72
Christian Berg Avatar answered Oct 04 '22 21:10

Christian Berg