懒惰序列的实现时序

时间:2012-11-19 13:40:34

标签: clojure lazy-sequences

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

3 个答案:

答案 0 :(得分:2)

因为map是懒惰的。它使用lazy-seq封面,pre-caches the result of rest。因此,当您的代码获取println序列的第一个值时,您会看到两个map语句。

另请参阅此博文:Lazy Sequences

答案 1 :(得分:2)

println在其implementation中使用[[x & xs] xs]解构表单。这相当于[x (first xs), xs (next xs)]next is less lazy than rest,因此它会在打印第一个项目之前实现这两个项目。

例如,

=> (defn fn1 [[x & xs]] nil)
#'user/fn1
=> (fn1 (map square '(1 2 3 4 5)))
Processing: 1
Processing: 2
nil

答案 2 :(得分: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给出的引用)。要完全实现懒惰序列(这通常不是一个好习惯,因为懒惰的seq可能是无限的,因此永远不会结束),你可以使用dorundoall

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

尽管它们看起来很相似但它们并不相同 - 请注意它们如何对待实现序列的头部。

根据这些知识,您可以使用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宏中。)< / p>

Clojure Programming一书中,对于'(1 2 3 4 5)的案例,您可能会喜欢这句话:

  

“大多数人只是在这种情况下使用矢量文字,其中包含成员表达式   将永远被评估。“

除了复制相关部分以支持此声明外,我宁愿推荐这本书,因为它值得花时间和金钱。