Clojure lazy-seq自然数字

时间:2017-01-10 21:59:24

标签: clojure

一个流行的在线教程给出了这个例子来构建自然数字:

(def infseq (map inc (iterate inc 0)))

例如,(take 5 infseq)给出:

1 2 3 4 5

我可以看到(iterate inc 0)的作用,但是,作为一个整体,我不明白究竟发生了什么。 (例如,这不是正常的函数定义。)

有人可以解释一下吗?

3 个答案:

答案 0 :(得分:2)

让我们打破这个表达:

(map inc (iterate inc 0)))

是具有此结构的列表(数据结构):

(function-to-call function-passed-as-first-srgument another-list-as-second-arg)

现在让我们从内到外检查它! 内心清单:

(iterate inc 0)

有这个结构

(function-to-call function-passed-as-first-argument number)
  • 被调用的函数是iterate,它通过跟踪它的内部状态来创建无限序列,并且每次需要一个新值来使序列变长时,它会将函数作为第一个传递参数并在当前状态下调用它。
  • 作为第一个参数传递的函数是inc,其中包含numbr和ads one
  • iterate的第三个参数是初始状态。它应该从哪里开始。

因此,当评估此内部表达式时,它将立即返回表示列表的数据结构,而不实际构建该列表。当从该列表中读取第一个值时,它将返回初始值0,当要求第二个值时,它将使用inc函数来提供数字1。如果再次需要第一个或第二个值,它们将按原样使用,而不是重新计算。

所以第一个参数代表一个产生所需数量的合约。这是原始表达式的第三个参数。

初始表达式采用该惰性列表并生成一个新的惰性列表。

这个新的惰性列表由map函数返回。

(map inc (0 1 2 3 4 ... as many as you read ...))

inc应用于每个这些,就像它的读取一样,并且只在它被读取的那一刻(实际上它将前面的20个左右的项目缓存得更快一点)导致这个序列:

((inc 0) (inc 1) (inc 2) (inc 3) ... as much as you read from the sequence ...)

其中包括:

(1 2 3 4 ... created lazily)

与这些等效表达式的结果相同,

(rest (range))

(iterate inc 1)

以及许多其他形式。

答案 1 :(得分:2)

出于示例的目的,我们可以按如下方式定义mapiterate

(defn iterate [f init]
  (lazy-cons init (iterate f (f init))))

(defn map [f [x & xs]]
  (lazy-cons (f x) (map f xs)))

...其中lazy-conscons的版本,在必须执行之前不会采取行动。它曾经是clojure.core的一部分,因此可能已定义:

(defmacro lazy-cons [x xs]
  `(lazy-seq (cons ~x ~xs)))

要理解这些定义,您需要掌握递归,懒惰,解构和宏:完成任务!但是这样做,你真的 了解clojure的序列库,包括iteratemap是如何工作的。这是我学习的方式。

iterate的定义有效。 map中的一个仅适用于单个无限序列参数。

答案 2 :(得分:1)

(take 5 (iterate inc 0)) => (0 1 2 3 4)

iterate在循环中重复应用inc函数。你从0开始,所以得到[0 1 2 3 ...]

(take 5 (map inc (iterate inc 0))) => (1 2 3 4 5)

(map inc <collection>)一次对集合中的每个项目应用inc,将之前的结果转换为[1 2 3 4 ...]

(take 5 (range)) => (0 1 2 3 4)

range没有任何args从0开始并永远计数,与第一个例子相同。

由于所有这些集合的长度都是无限的,我们需要(take 5 <collection>)之类的东西来限制打印的内容。