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 (开发者_StackOverflow中文版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?
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!
try adding doall in front: The for function creates a lazy sequence, as does take. They may not evaluate until you add a doall.
This little quirk is a common annoyance in Clojure, but makes sense once you get more comfortable with "thinking lazily".
Actually, it is working for me. The last example does print one hundred "Hello World"
, but the take
gets just one hundred nil
s that are also printed in the REPL. The first example works as it is:
user=> (take 100 (for [x (range 100000000) y (range 1000000) :while (< y x)] [x y]))
([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 actually printing, you can try a more easy loop:
(dotimes [i 100] (println "Hello World"))
Finally, if you use for
, you don't need the :while
, as the range already has 100 elements:
(take 100 (for [a (range 100)]
(println "Hello World")))
精彩评论