Clojure无限惰性序列,越界
我最近才开始学习Clojure,如果这有点初级,我深表歉意: 有人能给我解释一下以下两者的区别吗Clojure无限惰性序列,越界,clojure,sequence,lazy-evaluation,Clojure,Sequence,Lazy Evaluation,我最近才开始学习Clojure,如果这有点初级,我深表歉意: 有人能给我解释一下以下两者的区别吗 => (def a (lazy-cat [0] (map inc a) )) => (take 5 a) (0 1 2 3 4) 及 我希望第二个例子也能以同样的方式运行,使用b的第一个元素来计算第二个,然后第二个元素来计算第三个。我的理解是,clojure甚至不会尝试计算b的第三个元素,直到它已经为第二个元素赋值并将其打印在屏幕
=> (def a (lazy-cat
[0]
(map inc a)
))
=> (take 5 a)
(0 1 2 3 4)
及
我希望第二个例子也能以同样的方式运行,使用b的第一个元素来计算第二个,然后第二个元素来计算第三个。我的理解是,clojure甚至不会尝试计算b的第三个元素,直到它已经为第二个元素赋值并将其打印在屏幕上
我希望有人能解释一下幕后到底发生了什么
谢谢:)查看
take
的源代码:
(defn take
"Returns a lazy sequence of the first n items in coll, or all items if
there are fewer than n."
{:added "1.0"
:static true}
[n coll]
(lazy-seq
(when (pos? n)
(when-let [s (seq coll)]
(cons (first s) (take (dec n) (rest s)))))))
现在对第一个案例进行分析。没有机会寻址超出边界的数组
对于第二个示例,您正在对尚未扩展到n个元素的序列调用
nth
。b将尝试将0与依赖于不存在的元素的序列连接起来。此行为的原因是最简单的(map f colls)
情况下的map
函数实现。请看区别:
user=> (def b (lazy-cat [0] (map (fn [i _] (inc (nth b i))) (range) (range))))
#'user/b
user=> (take 5 b)
(0 1 2 3 4)
这有点让人困惑,但让我解释一下发生了什么。那么,为什么map
的第二个参数会改变行为:
回答:分块序列的优化原因
user=> (chunked-seq? (seq (range)))
true
因此,值将“预先计算”:
当然,在你的例子中,这个“预先计算”失败了,因为
索引自动边界异常刚刚意识到我忘了感谢你。这是一个很大的帮助:)
(defn map
...
([f coll]
(lazy-seq
(when-let [s (seq coll)]
(if (chunked-seq? s)
(let [c (chunk-first s)
size (int (count c))
b (chunk-buffer size)]
(dotimes [i size]
(chunk-append b (f (.nth c i))))
(chunk-cons (chunk b) (map f (chunk-rest s))))
(cons (f (first s)) (map f (rest s)))))))
([f c1 c2]
(lazy-seq
(let [s1 (seq c1) s2 (seq c2)]
(when (and s1 s2)
(cons (f (first s1) (first s2))
(map f (rest s1) (rest s2)))))))
...
user=> (chunked-seq? (seq (range)))
true
user=> (def b (lazy-cat [0] (map print (range))))
#'user/b
user=> (take 5 b)
(0123456789101112131415161718192021222324252627282930310 nil nil nil nil)