Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/33.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Recursion 递归在实现中的应用;分区;功能_Recursion_Clojure_Tail Recursion_Lazy Sequences - Fatal编程技术网

Recursion 递归在实现中的应用;分区;功能

Recursion 递归在实现中的应用;分区;功能,recursion,clojure,tail-recursion,lazy-sequences,Recursion,Clojure,Tail Recursion,Lazy Sequences,我随机阅读了Clojure的源代码,发现: 这样做有什么原因吗?分区是懒惰的。对分区的递归调用发生在惰性seq的主体内。因此,它不会立即被调用,而是在一个特殊的seq-able对象中返回,以便在需要时进行评估,并缓存到目前为止实现的结果。堆栈深度一次仅限于一次调用 不带lazy seq的recur可以用来创建一个渴望的版本,但您不希望在长度不确定的序列上使用它,就像在内核中使用版本一样。所有的延迟函数都是这样编写的。partition的这种实现将在不调用lazy seq的情况下使堆栈崩溃,从而获

我随机阅读了Clojure的源代码,发现:


这样做有什么原因吗?

分区是懒惰的。对
分区的递归调用发生在
惰性seq
的主体内。因此,它不会立即被调用,而是在一个特殊的seq-able对象中返回,以便在需要时进行评估,并缓存到目前为止实现的结果。堆栈深度一次仅限于一次调用


不带lazy seq的recur可以用来创建一个渴望的版本,但您不希望在长度不确定的序列上使用它,就像在内核中使用版本一样。

所有的延迟函数都是这样编写的。
partition
的这种实现将在不调用
lazy seq
的情况下使堆栈崩溃,从而获得足够大的序列


如果您对
recur
的工作方式更感兴趣,请阅读一些关于TCO(尾部调用优化)的内容。当您使用尾部递归时,这意味着您可以跳出当前函数调用而不丢失任何内容。在这种实现中,您将无法这样做,因为在下一次调用
分区时,您正在
cons
-ing
p
。处于尾位意味着你是最后一个被呼叫的对象。在实现中,
cons
处于尾部位置<代码>重复
仅对尾部位置有效,以保证总体拥有成本。

以@A.Webb的回答和@amalloy的评论为基础:

recur
不是调用函数的简写,也不是函数。它是执行尾部调用优化的一种特殊形式(在另一种语言中称为语法)

尾部调用优化是一种允许在不破坏堆栈的情况下使用递归的技术(没有它,每个递归调用都会将其调用帧添加到堆栈中)。它还没有在Java中本机实现,这就是为什么在Clojure中使用
recur
标记尾部调用的原因

使用
lazy seq
的递归是不同的,因为它通过将递归调用包装在闭包中来延迟递归调用。这意味着,根据
lazy-seq
实现的函数调用(尤其是此类函数中的每个递归调用)会(立即)返回
LazySeq
序列,其计算会被延迟,直到迭代为止


为了说明@amalloy的评论,即
重复出现
和懒惰是相互排斥的,下面是一个
过滤器
的实现,它使用了这两种技术:

(defn filter [pred coll]
  (letfn [(step [pred coll]
            (when-let [[x & more] (seq coll)]
              (if (pred x)
                (cons x (lazy-seq (step pred more))) ;; lazy recursive call
                (recur pred more))))]                ;; tail call
    (lazy-seq (step pred coll))))

(filter even? (range 10))
;; => (0 2 4 6 8)

这两种技术可以在同一个函数中使用,但不能用于同一个递归调用;如果对
step
的惰性递归调用使用
recur
,函数将不会编译,因为在这种情况下,
recur
不会处于尾部调用位置(尾部调用的结果不会直接返回,而是会传递给
lazy seq
)。

这就是我理解您答案的方式:1。结果可能是无限长的,所以我们更喜欢在这里让它变懒。2.如果它是惰性的,那么使用recur或直接调用tail递归实际上是相同的。我的理解是正确的?绝对不是:惰性和尾部递归(使用
recur
)是产生序列的相互排斥的方式。如果您试图在此处用
recur
替换递归调用,它将无法编译,因为
recur
将不在尾部位置。@amalloy Hi-amalloy:1。lazy和
recur
是互斥的,因为lazy只计算一部分,而recur同时计算全部?2.我不明白为什么它不能编译?由于
recur
的语法,感谢您澄清我的困惑!我真的希望这样可以让我接受多个答案!谢谢你澄清我的困惑!我真的希望这样可以让我接受多个答案!
(defn filter [pred coll]
  (letfn [(step [pred coll]
            (when-let [[x & more] (seq coll)]
              (if (pred x)
                (cons x (lazy-seq (step pred more))) ;; lazy recursive call
                (recur pred more))))]                ;; tail call
    (lazy-seq (step pred coll))))

(filter even? (range 10))
;; => (0 2 4 6 8)