Reputation: 2625
(defn square [x]
(do
(println (str "Processing: " x))
(* x x)))
(println (map square '(1 2 3 4 5)))
Why is the output
(Processing: 1
Processing: 2
1 Processing: 3
4 Processing: 4
9 Processing: 5
16 25)
not
(Processing: 1
1 Processing: 2
4 Processing: 3
9 Processing: 4
16 Processing: 5
25)
?
Upvotes: 5
Views: 247
Reputation: 74619
Are you like me to learn with code snippets? Here are some.
Let's have a look at the documentation of map
.
user=> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll, until any one of the colls is
exhausted. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments.
nil
map
returns a lazy sequence (you should already read the references given by @noahz). To fully realize the lazy sequence (it's often not a good practice as the lazy seq might be infinite and hence never end) you can use dorun
or doall
.
user=> (doc dorun)
-------------------------
clojure.core/dorun
([coll] [n coll])
When lazy sequences are produced via functions that have side
effects, any effects other than those needed to produce the first
element in the seq do not occur until the seq is consumed. dorun can
be used to force any effects. Walks through the successive nexts of
the seq, does not retain the head and returns nil.
nil
user=> (doc doall)
-------------------------
clojure.core/doall
([coll] [n coll])
When lazy sequences are produced via functions that have side
effects, any effects other than those needed to produce the first
element in the seq do not occur until the seq is consumed. doall can
be used to force any effects. Walks through the successive nexts of
the seq, retains the head and returns it, thus causing the entire
seq to reside in memory at one time.
nil
Although they seem similar they are not - mind the difference with how they treat the head of the realized sequence.
With the knowledge, you can influence the way the map lazy sequence behaves with doall
.
user=> (defn square [x]
#_=> (println (str "Processing: " x))
#_=> (* x x))
#'user/square
user=> (doall (map square '(1 2 3 4 5)))
Processing: 1
Processing: 2
Processing: 3
Processing: 4
Processing: 5
(1 4 9 16 25)
As you might've noticed I also changed the definition of the square
function as you don't need do
inside the function (it's implicit with the defn
macro).
In the Clojure Programming book, there's the sentence you may like for the case of '(1 2 3 4 5)
:
"Most people simply use a vector literal for such cases, within which member expressions will always be evaluated."
Instead of copying the relevant sections to support this statement, I'd rather recommend the book as it's worth the time and money.
Upvotes: 1
Reputation: 2625
println
uses [[x & xs] xs]
destructuring form in its implementation. This is equivelent to [x (first xs), xs (next xs)]
and next
is less lazy than rest
, so it realizes the two items before printing the first.
For example,
=> (defn fn1 [[x & xs]] nil)
#'user/fn1
=> (fn1 (map square '(1 2 3 4 5)))
Processing: 1
Processing: 2
nil
Upvotes: 2
Reputation: 10311
Because map
is lazy. It uses lazy-seq
under the covers, which pre-caches the result of rest
. So you see the two println
statements appear when your code grabs the first value of the map
sequence.
See also this blog post: Lazy Sequences
Upvotes: 2