If I understand correctly Clojure can return lists (as in other Lisps) but also vectors and sets.
What I don’t really get is why there’s not always a collection that is returned.
For example if I take the following code:
(loop [x 128]
(when (> x 1)
(println x)
(recur (/ x 2))))
It does print 128 64 32 16 8 4 2. But that’s only because println is called and println has the side-effect (?) of printing something.
So I tried replacing it with this (removing the println):
(loop [x 128]
(when (> x 1)
x
(recur (/ x 2))))
And I was expecting to get some collecting (supposedly a list), like this:
(128 64 32 16 8 4 2)
but instead I’m getting nil.
I don’t understand which determines what creates a collection and what doesn’t and how you switch from one to the other. Also, seen that Clojure somehow encourages a “functional” way of programming, aren’t you supposed to nearly always return collections?
Why are so many functions that apparently do not return any collection? And what would be an idiomatic way to make these return collections?
For example, how would I solve the above problem by first constructing a collection and then iterating (?) in an idiomatic way other the resulting list/vector?
First I don’t know how to transform the loop so that it produces something else than nil and then I tried the following:
(reduce println '(1 2 3))
But it prints “1 2nil 3nil” instead of the “1 2 3nil” I was expecting.
I realize this is basic stuff but I’m just starting and I’m obviously missing basic stuff here.
(P.S.: retag appropriately, I don’t know which terms I should use here)
A few other comments have pointed out that when doesn’t really work like if – but I don’t think that’s really your question.
The loop and recur forms create an iteration – like a for loop in other languages. In this case, when you are printing, it is indeed just for the side effects. If you want to return a sequence, then you’ll need to build one:
In this case, I replaced the spot where you were calling printf with a recur and a form that adds x to the front of that accumulator. In the case that x is less than 1, the code returns the accumulator – and thus a sequence. If you want to add to the end of the vector instead of the front, change it to conj:
You were getting nil because that was the result of your expression — what the final println returned.
Does all this make sense?
reduce is not quite the same thing — it is used to reduce a list by repeatedly applying a binary function (a function that takes 2 arguments) to either an initial value and the first element of a sequence, or the first two elements of the sequence for the first iteration, then subsequent iterations are passed the result of the previous iteration and the next value from the sequence. Some examples may help:
This executes the following:
Reduce will result in whatever the final result is from the binary function being executed — in this case we’re reducing the numbers in the sequence into the sum of all the elements.
You can also supply an initial value:
Which executes the following:
HTH,
Kyle