为什么缺点在这方面与懒惰-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]噢哦! 问涉及计算器上堆栈溢出的问题。
(conj collection item)
增加item
来collection
。 要做到这一点,它需要实现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
? (如何Cons
和LazySeq
对象协同工作,以形成懒惰序列将变得更清楚的下方。)看的线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的结果。