这是很多问题!
seq API 实际上如何与 LazySeq 配合使用?
如果你看一下LazySeq https://github.com/clojure/clojure/blob/206d94c9cfb01f981a157142929c9456c547d6ea/src/jvm/clojure/lang/LazySeq.java#L17的类源代码你会注意到它实现了ISeq https://github.com/clojure/clojure/blob/206d94c9cfb01f981a157142929c9456c547d6ea/src/jvm/clojure/lang/ISeq.java接口提供类似的方法first
, more
and next
.
功能类似于map
, take
and filter
是使用构建的lazy-seq
(它们产生惰性序列)和first
and rest
(这又使用more
)这就是他们如何使用惰性序列作为输入集合 - 通过使用first
and more
的实施LazySeq
class.
下面的表达式实际上发生了什么?
(reduce + (take 3 (map inc (rep 5))))
关键是看如何LazySeq.first
作品。它将调用包装的函数来获取并存储结果。在您的情况下,它将是以下代码:
(cons n (rep n))
因此它将是一个缺点单元格n
作为它的值和另一个LazySeq
实例(递归调用的结果rep
) 就像它一样rest
部分。它将成为这个的实现价值LazySeq
对象和first
将返回缓存的 cons 单元格的值。
你打电话时more
在此之上,它将以同样的方式确保特定的价值LazySeq
对象被实现(或重用记忆值)并调用more
在其上(在本例中more
在包含另一个的 cons 单元上LazySeq
目的)。
一旦您获得另一个实例LazySeq
对象与more
当你打电话时,故事会重复first
on it.
map
and take
将创建另一个lazy-seq
那会打电话first
and more
集合的数量作为他们的参数传递(只是另一个惰性序列),所以这将是类似的故事。区别仅在于值如何传递到cons
生成(例如调用f
到通过以下方式获得的值first
调用于LazySeq
映射到的值map
而不是像这样的原始值n
在你的rep
功能)。
With reduce
它有点简单,因为它会使用loop
with first
and more
迭代输入的惰性序列并应用归约函数来产生最终结果。
因为实际的实现看起来像map
and take
我鼓励您检查他们的源代码 - 它很容易理解。
seq API 如何处理不同的集合类型(例如惰性 seq 和持久向量)?
正如刚才提到的,map
, take
和其他功能的工作方式first
and rest
(提醒 -rest
是在之上实现的more
)。因此我们需要解释如何first
and rest
/more
可以使用不同的集合类型:它们检查集合是否实现ISeq
(然后它直接实现这些功能)或者他们尝试创建一个seq
查看收集并整理其实施first
and more
.
是否可以生成嵌套的无限数据结构?
这绝对是可能的,但我不确定您想要获得的确切数据形状是什么。你的意思是得到一个惰性序列,它生成另一个序列作为它的值(而不是像这样的单个值n
在你的rep
)但将其作为平面序列返回?
(defn nested-cons [n]
(lazy-seq (cons (repeat n n) (nested-cons (inc n)))))
(take 3 (nested-cons 1))
;; => ((1) (2 2) (3 3 3))
宁愿返回(1 2 2 3 3 3)
?
对于这种情况你可以使用concat
代替cons
它创建两个或多个序列的惰性序列:
(defn nested-concat [n]
(lazy-seq (concat (repeat n n) (nested-concat (inc n)))))
(take 6 (nested-concat 1))
;; => (1 2 2 3 3 3)
这有什么实际区别吗
(defn rep [n]
(lazy-seq (cons n (rep n))))
和这个?
(defn rep [n]
(cons n (lazy-seq (rep n))))
在这种特殊情况下并非如此。但是,如果 cons 单元格不包装原始值,而是包装计算它的函数调用的结果,则后一种形式并不是完全惰性的。例如:
(defn calculate-sth [n]
(println "Calculating" n)
n)
(defn rep1 [n]
(lazy-seq (cons (calculate-sth n) (rep1 (inc n)))))
(defn rep2 [n]
(cons (calculate-sth n) (lazy-seq (rep2 (inc n)))))
(take 0 (rep1 1))
;; => ()
(take 0 (rep2 1))
;; Prints: Calculating 1
;; => ()
因此,即使您可能不需要它,后一种形式也会评估其第一个元素。