Frage

(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)

?

War es hilfreich?

Lösung 2

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

Andere Tipps

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

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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top