-->

Clojure的利弊VS懒-seq的连词(clojure cons vs conj with laz

2019-07-31 13:35发布

为什么缺点在这方面与懒惰-seq的工作,但连词不?

这工作:

(defn compound-interest [p i]
   (cons p (lazy-seq (compound-interest (* p (+ 1 i)) i))))

此不确实(它给出了一个堆栈溢出[1]除外):

(defn compound-interest2 [p i]
   (conj (lazy-seq (compound-interest2 (* p (+ 1 i)) i)) p))

[1]噢哦! 问涉及计算器上堆栈溢出的问题。

Answer 1:

(conj collection item)增加itemcollection 。 要做到这一点,它需要实现collection 。 (我会解释为什么下面。)因此,递归调用立即发生,而不是被推迟。

(cons item collection)创建序列开头item ,其次是一切collection 。 显著,它并不需要实现collection 。 因此,递归调用将被延期(因为使用的lazy-seq ),直到有人试图得到结果序列的尾部。

我将解释如何在内部工作:

cons的实际返回clojure.lang.Cons对象,这是懒惰的序列而成的。 conj返回您传递给它(这是否是一个列表,向量,或任何其他)同一类型的集合。 conj这是否使用集合本身多态的Java方法调用。 (参见线524 clojure/src/jvm/clojure/lang/RT.java 。)

当Java方法调用发生在上会发生什么clojure.lang.LazySeq这是由返回的对象lazy-seq ? (如何ConsLazySeq对象协同工作,以形成懒惰序列将变得更清楚的下方。)看的线98 clojure/src/jvm/clojure/lang/LazySeq.java 。 请注意,它调用一个方法叫做seq 。 这是实现价值LazySeq (跳到第55行的详细信息)。

所以,你可以说, conj需要知道到底是什么类型的集合你通过它,但cons没有。 cons只是要求“收藏”的说法是一种ISeq

需要注意的是Cons Clojure中的对象在其它的Lisp是不同于“缺点细胞” -在大多数的Lisp,一个“缺点”只是保持2个指向其他任意的对象的对象。 所以,你可以使用利弊细胞构建树木,等等。 甲Clojure的Cons接受一个任意Object的头部和一个ISeq作为尾。 因为Cons本身实现ISeq ,你可以建立序列出的Cons对象,但他们可以也指向向量或列表等(请注意,“名单”中Clojure是一种特殊类型( PersistentList ),而不是从内置Cons的对象。) clojure.lang.LazySeq 实现ISeq ,所以它可被用作一个的尾部中的Lisp(“CDR”) Cons 。 一个LazySeq持有到一些代码, 计算结果为基准ISeq一番,但直到需要它实际上并没有评估的代码,并且它评估的代码后,将缓存返回ISeq和代表它。

......这一切开始有意义吗? 你得到的序列是如何工作偷懒的想法? 基本上,你开始一个LazySeq 。 当LazySeq得以实现,它的计算结果为一个Cons ,它指向另一LazySeq 。 当实现一个...你的想法。 所以,你得到的链LazySeq对象,各持(和委派到)一个Cons

关于Clojure中的“conses之外”和“名单”之间的区别,“名单”( PersistentList对象)包含一个缓存的“长度”字段,这样他们就可以应对count为O(1)时间。 这不会在其他的Lisp的工作,因为在大多数的Lisp,“名单”是可变的。 但是Clojure中他们是不变的,所以缓存的长篇作品。

Cons Clojure中的对象没有缓存的长度-如果他们做到了,他们怎么可能被用于实现懒(甚至是无限的)序列? 如果你试图把count一的Cons ,它只是调用count尾巴上,然后递增1的结果。



文章来源: clojure cons vs conj with lazy-seq