Clojure懒惰序列使用

时间:2011-02-14 12:46:20

标签: clojure functional-programming lazy-evaluation

我无法理解如何在Clojure中创建一个懒惰的序列。

宏的文档对我来说一点也不清楚:

  

用法:( lazy-seq& body)   采用返回ISeq或nil的表达式体,并产生   一个Seqable对象,它将仅在第一次seq时调用主体   被调用,并将缓存结果并在随后的所有内容中返回   seq电话。

我见过的所有例子似乎都是这样的:

; return everything in the sequence starting at idx n
(defn myseq-after-n [n]
  (...)
)

(def my-lazy-seq
  (lazy-seq (conj [init-value] (myseq-after-n 2)))
)

所以,我没有得到的第一件事是,因为lazy-seq不在调用conj之外,它是如何阻止conj在评估时生成无限序列的?

我的第二个问题是,懒惰的序列定义总是采用这种一般形式吗?

1 个答案:

答案 0 :(得分:56)

lazy-seq调用只是在第一次访问时执行,然后缓存并在将来再次调用时返回相同的结果。

如果要使用它来构建长(甚至无限)序列,则需要在返回的序列中递归嵌套其他lazy-seq调用。这是我能想到的最简单的案例:

(defn ints-from [n]
  (cons n (lazy-seq (ints-from (inc n)))))

(take 10 (ints-from 7))
=> (7 8 9 10 11 12 13 14 15 16)

任何(ints-from n)调用产生一个以n开头的序列,后跟一个惰性序列(ints-from(inc n))。这是一个无限的列表,但这不是问题,因为lazy-seq确保(int-from(inc n))仅在需要时被调用。您可以在没有lazy-seq的情况下尝试完全相同的代码,并且您很快就会得到StackOverflowError。

lazy-seq只是创建延迟序列的众多可能方法之一,而且通常不是最方便的。以下是创建延迟序列的一些其他有趣/有用的方法:

; range is an easy way to get an infinite lazy sequence of integers, starting with zero     
(take 10 (range))
=> (0 1 2 3 4 5 6 7 8 9)

; map produces lazy sequences, so the following is lazy 
(take 10 (map #(* % %) (range)))
=> (0 1 4 9 16 25 36 49 64 81)

; iterate is a good way of making infinite sequenes of the form x, f(x), f(f(x))..... 
(take 10 (iterate (partial * 2) 1))
=> (1 2 4 8 16 32 64 128 256 512)