Clojure。为什么需要包装器lazy-seq?

时间:2019-04-08 16:44:28

标签: clojure

为什么需要包装lazy-cons?有两个函数的结果相同。

(defn seq1 [s]
  (lazy-seq
    (when-let [x (seq s)]
      (cons (first x) (seq1 (rest x))))))


(defn seq2 [s]
    (when-let [x (seq s)]
      (cons (first x) (seq2 (rest x)))))

两种情况下我得到的结果都是相同的,没有分块的序列。

repl.core=> (first (map println (seq1 (range 1000))))
0
nil
repl.core=> (first (map println (seq2 (range 1000))))
0
nil
repl.core=> (chunked-seq? (seq2 (range 1000)))
false
repl.core=> (chunked-seq? (seq1 (range 1000)))
false

1 个答案:

答案 0 :(得分:2)

第一个是懒惰。它仅根据需要评估序列的元素。但是第二个是严格的,并立即遍历整个序列。如果您在每个通话中添加一些println通话,则可以看到这一点:

(defn seq1 [s]
  (lazy-seq
    (when-let [x (seq s)]
      (println "Seq1" (first x))
      (cons (first x) (seq1 (rest x))))))


(defn seq2 [s]
  (when-let [x (seq s)]
    (println "Seq2" (first x))
    (cons (first x) (seq2 (rest x)))))

(->> (range 10)
     (seq1)
     (take 5))
Seq1 0
Seq1 1
Seq1 2
Seq1 3
Seq1 4 ; Only iterated over what was asked for
=> (0 1 2 3 4) 

(->> (range 10)
     (seq2)
     (take 5))
Seq2 0
Seq2 1
Seq2 2
Seq2 3
Seq2 4
Seq2 5
Seq2 6
Seq2 7
Seq2 8
Seq2 9 ; Iterated over everything immediately
=> (0 1 2 3 4) 

因此,要回答这个问题,仅当您打算仅根据需要进行序列的迭代时,才需要lazy-seq。如果您认为您不需要整个序列,或者该序列是无限的,或者您想使用mapfilter对多个转换进行排序,则最好使用惰性解决方案。如果您可能在某个时候需要整个序列和/或需要快速随机访问,请使用严格的解决方案。