5
(defn square [x]
  (do
    (println (str "Processing: " x))
    (* x x)))

(println (map square '(1 2 3 4 5)))

为什么是输出

(Processing: 1 
Processing: 2 
1 Processing: 3 
4 Processing: 4 
9 Processing: 5 
16 25)

不是

(Processing: 1
1 Processing: 2
4 Processing: 3 
9 Processing: 4 
16 Processing: 5 
25)

?

4

3 回答 3

2

因为map很懒。它lazy-seq在幕后使用,预先缓存rest. println因此,当您的代码获取map序列的第一个值时,您会看到两条语句出现。

另请参阅此博客文章:惰性序列

于 2012-11-19T14:45:30.817 回答
2

println在其实现中使用[[x & xs] xs]解构形式。这等效于并且不那么懒惰,因此它在打印第一项之前实现了这两项。[x (first xs), xs (next xs)]nextrest

例如,

=> (defn fn1 [[x & xs]] nil)
#'user/fn1
=> (fn1 (map square '(1 2 3 4 5)))
Processing: 1
Processing: 2
nil
于 2012-11-20T03:45:59.163 回答
1

你和我一样用代码片段来学习吗?这里有一些。

让我们看一下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返回一个惰性序列(您应该已经阅读了@noahz 给出的引用)。要完全实现惰性序列(这通常不是一个好习惯,因为惰性序列可能是无限的,因此永远不会结束),您可以使用dorunor 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

尽管它们看起来相似,但它们并不相似——请注意它们对待已实现序列的头部的方式不同。

有了这些知识,您可以影响 map 惰性序列的行为方式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)

正如您可能已经注意到的那样,我还更改了square函数的定义,因为您不需要do在函数内部(它隐含在defn宏中)。

Clojure Programming book 中,您可能会喜欢以下语句'(1 2 3 4 5)

“大多数人只是在这种情况下使用向量文字,其中成员表达式将始终被评估。”

与其复制相关部分来支持这一说法,我更愿意推荐这本书,因为它值得花时间和金钱。

于 2012-11-20T10:52:54.083 回答