clojure cons vs conj与懒惰序列 [英] clojure cons vs conj with lazy-seq

查看:119
本文介绍了clojure cons vs conj与懒惰序列的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为什么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 对象,但它们也可以指向矢量或列表等。(请注意,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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆