Why doesn't my code print as it should?

I am trying to create a for in Clojure.

I am following the cheat sheet from the Clojure website .

eg:

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

I am 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 does not print Hello World 100 times. Why not?

+6
source share
3 answers

The most important thing you need to know about is that the sequences in Clojure are lazy. This means that elements in a sequence are evaluated only when they are needed. This allows you to work with infinite sequences.

In most cases, this is what you want, but it can be confusing when you are really not interested in the values โ€‹โ€‹of the elements of the sequence, but in the side effects of the functions that create the elements of the sequence. In your example, the sequence consists of 100 return values โ€‹โ€‹of the println function, i.e. 100 times nil - not very interesting. But the println function has the side effect of printing "Hello World" on standard output.

The problem is that if you never do anything with elements in a sequence, println functions are never evaluated and lines are not printed. It works in REPL, because P in REPL means print - it returns the return value of the expression you entered. To print the entire sequence, it must be evaluated, so you see a bunch of "Hello World", but also a bunch of nils . If you run the code outside of REPL (without using the return value), you will not see Hello Worlds.

If you are interested in the side effects of your element generating functions, you can use doall or doseq . doall forcibly evaluates the entire sequence and returns it:

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

doseq does not return a sequence (it returns nil ) and has syntax like for :

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

Also note that you really only need to specify the desired counter (100) only once. The range function already produces a sequence with 100 elements in it. Suggestion :while in your code is redundant, as is the take function (there are not many choices of the first 100 elements from a sequence of 100 elements).

Hope this helps!

+12
source

try adding doall in front: the for function creates a lazy sequence as required. They may not be evaluated until you add doall.

This little quirk is a common annoyance in Clojure, but it makes sense when you become more comfortable with lazy thinking.

+2
source

Actually, it works for me. The last example prints a hundred "Hello World" , but take receives only one hundred nil , which are also printed in REPL. The first example works like this:

user=> (take 100 (for [x (range 100000000) y (range 1000000) :while (< yx)] [xy])) ([1 0] [2 0] [2 1] [3 0] [3 1] [3 2] [4 0] [4 1] [4 2] [4 3] [5 0] [5 1] [5 2] [5 3] [5 4] [6 0] [6 1] [6 2] [6 3] [6 4] [6 5] [7 0] [7 1] [7 2] [7 3] [7 4] [7 5] [7 6] [8 0] [8 1] [8 2] [8 3] [8 4] [8 5] [8 6] [8 7] [9 0] [9 1] [9 2] [9 3] [9 4] [9 5] [9 6] [9 7] [9 8] [10 0] [10 1] [10 2] [10 3] [10 4] [10 5] [10 6] [10 7] [10 8] [10 9] [11 0] [11 1] [11 2] [11 3] [11 4] [11 5] [11 6] [11 7] [11 8] [11 9] [11 10] [12 0] [12 1] [12 2] [12 3] [12 4] [12 5] [12 6] [12 7] [12 8] [12 9] [12 10] [12 11] [13 0] [13 1] [13 2] [13 3] [13 4] [13 5] [13 6] [13 7] [13 8] [13 9] [13 10] [13 11] [13 12] [14 0] [14 1] [14 2] [14 3] [14 4] [14 5] [14 6] [14 7] [14 8])

For actual printing, you can try a simpler loop:

 (dotimes [i 100] (println "Hello World")) 

Finally, if you use for , you do not need :while , since the range already contains 100 elements:

 (take 100 (for [a (range 100)] (println "Hello World"))) 
+1
source

Source: https://habr.com/ru/post/896833/


All Articles