clojure cons vs conj与懒惰序列 [英] clojure cons vs conj with lazy-seq
问题描述
为什么cons与lazy-seq一起在这种情况下起作用,而conj却不起作用?
Why does cons work in this context with lazy-seq, but conj doesn't?
这可行:
(defn compound-interest [p i]
(cons p (lazy-seq (compound-interest (* p (+ 1 i)) i))))
这不是(它会产生堆栈溢出[1]异常):
This doesn't (it gives a stack overflow[1] exception):
(defn compound-interest2 [p i]
(conj (lazy-seq (compound-interest2 (* p (+ 1 i)) i)) p))
[1]哦,是的!提出一个涉及stackoverflow上的堆栈溢出的问题。
[1] Oh ya! Asking a question involving a stack overflow on stackoverflow.
推荐答案
(conj收集项)
将项目
添加到集合
。为此,它需要实现 collection
。 (我将在下面解释原因。)因此,递归调用会立即发生,而不是被推迟。
(conj collection item)
adds item
to collection
. To do that, it needs to realize collection
. (I'll explain why below.) So the recursive call happens immediately, rather than being deferred.
(缺点项目集合)
创建一个序列,该序列以 item
开头,后跟 collection
中的所有内容。重要的是,它不需要实现集合
。因此,递归调用将被推迟(因为使用了 lazy-seq
),直到有人试图获得结果序列的尾部为止。
(cons item collection)
creates a sequence which begins with item
, followed by everything in collection
. Significantly, it doesn't need to realize collection
. So the recursive call will be deferred (because of using lazy-seq
) until somebody tries to get the tail of the resulting sequence.
我将解释其内部运作方式:
I'll explain how this works internally:
cons
实际上会返回 clojure.lang.Cons
对象,它是由惰性序列组成的。 conj
返回与您传递的类型相同的集合(无论是列表,向量还是其他)。 conj
使用对集合本身的多态Java方法调用来完成此操作。 (请参见第524行, clojure / src / jvm / clojure / lang / RT.java
。)
cons
actually returns a clojure.lang.Cons
object, which is what lazy sequences are made of. conj
returns the same type of collection which you pass it (whether that is a list, vector, or whatever else). conj
does this using a polymorphic Java method call on the collection itself. (See line 524 of clojure/src/jvm/clojure/lang/RT.java
.)
调用该Java方法时会发生什么发生在 lazy-seq
返回的 clojure.lang.LazySeq
对象上吗? ( Cons
和 LazySeq
对象如何协同工作以形成惰性序列将在下面变得更加清楚。)请参见第98行, clojure / src / jvm / clojure / lang / LazySeq.java
。注意,它调用了名为 seq
的方法。这就是实现 LazySeq
(跳转到第55行了解详情)。
What happens when that Java method call happens on the clojure.lang.LazySeq
object which is returned by lazy-seq
? (How Cons
and LazySeq
objects work together to form lazy sequences will become clearer below.) Look at line 98 of clojure/src/jvm/clojure/lang/LazySeq.java
. Notice it calls a method called seq
. This is what realizes the value of the LazySeq
(jump to line 55 for the details).
所以您可以说 conj
需要确切地知道您通过了哪种收集,而 cons
却没有。 cons
仅要求 collection参数为 ISeq
。
So you could say that conj
needs to know exactly what kind of collection you passed it, but cons
doesn't. cons
just requires that the "collection" argument is an ISeq
.
请注意,Clojure中的 Cons
对象与其他Lisps中的 cons单元格不同-在大多数Lisps中, cons只是一个拥有2个对象的对象指向其他任意对象的指针。因此,您可以使用cons单元构建树,依此类推。 Clojure Cons
以任意 Object
作为头部,以及 ISeq
作为尾巴。由于 Cons
本身实现了 ISeq
,因此您可以在 Cons $ c $之外构建序列c>对象,但它们也可以指向矢量或列表等。(请注意,Clojure中的列表是一种特殊类型(
PersistentList
),并且不是是由 Cons
对象构建的。) clojure.lang.LazySeq
实现 ISeq
,因此它可用作 Cons
。 LazySeq
包含对某些代码的引用,这些代码评估为某种 ISeq
,但是它实际上并没有对代码进行评估,直到需要时才对代码进行评估,然后对返回的 ISeq
进行缓存并委托给它。
Note that Cons
objects in Clojure are different from "cons cells" in other Lisps -- in most Lisps, a "cons" is just an object which holds 2 pointers to other arbitrary objects. So you can use cons cells to build trees, and so on. A Clojure Cons
takes an arbitrary Object
as head, and an ISeq
as tail. Since Cons
itself implements ISeq
, you can build sequences out of Cons
objects, but they can just as well point to vectors, or lists, etc. (Note that a "list" in Clojure is a special type (PersistentList
), and is not built from Cons
objects.) clojure.lang.LazySeq
also implements ISeq
, so it can be used as the tail ("cdr" in Lisps) of a Cons
. A LazySeq
holds a reference to some code which evaluates to an ISeq
of some kind, but it doesn't actually evaluate that code until required, and after it does evaluate the code, it caches the returned ISeq
and delegates to it.
...这一切都开始有意义了吗?您是否了解惰性序列如何工作?基本上,您从 LazySeq
开始。当实现 LazySeq
时,其结果为 Cons
,它指向另一个 LazySeq
。当实现这一目标时,您就明白了。因此,您获得了一个由 LazySeq
对象组成的链,每个对象都持有(并委托)一个 Cons
。
...is this all starting to make sense? Do you get the idea of how lazy sequences work? Basically, you start with a LazySeq
. When the LazySeq
is realized, it evaluates to a Cons
, which points to another LazySeq
. When that one is realized... you get the idea. So you get a chain of LazySeq
objects, each holding (and delegating to) a Cons
.
关于Clojure中 conses和 lists之间的区别, lists( PersistentList
对象)包含一个缓存的 length字段,以便他们可以在O(1)时间内响应 count
。这在其他Lips中不起作用,因为在大多数Lips中,列表是可变的。但是在Clojure中它们是不可变的,因此可以缓存长度。
About the difference between "conses" and "lists" in Clojure, "lists" (PersistentList
objects) contain a cached "length" field, so they can respond to count
in O(1) time. This wouldn't work in other Lisps, because in most Lisps, "lists" are mutable. But in Clojure they are immutable, so caching the length works.
缺点
Clojure中的对象 don' t 有一个缓存的长度-如果有,那么如何将它们用于实现惰性(甚至无限)序列?如果您尝试使用缺点
的计数
,它只会调用 count
放在其尾部,然后将结果加1。
Cons
objects in Clojure don't have a cached length -- if they did, how could they be used to implement lazy (and even infinite) sequences? If you try to take the count
of a Cons
, it just calls count
on its tail, and then increments the result by 1.
这篇关于clojure cons vs conj与懒惰序列的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!