循环/递归和递归之间有什么区别?

res*_*ive 3 recursion tail-recursion clojure tail-call-optimization

我在Clojure文档中找不到答案。我是Clojure的新手,似乎您可以recur两种不同的方式使用并且基本上得到相同的结果。

范例1:

(defn my-function [num]
  (if (> num 10)
    num
    (recur (+ num 1))))
Run Code Online (Sandbox Code Playgroud)

范例2:

(defn my-function [num]
  (loop [cnt num]
    (if (> cnt 10)
      cnt
      (recur (+ cnt 1)))))
Run Code Online (Sandbox Code Playgroud)

据我所知,这两种形式似乎做的完全相同。我知道,recur总体上讲,这样做的原因是好的,因为在适当的情况下,编译器可以破解某种伪尾部调用优化方法,我非常喜欢并且希望尽可能多地利用它。所以这是我的问题:

  1. loop如果recur没有它,似乎什么也可以使用?
  2. 是否loop仅创建“递归范围”,就像let创建迷你范围一样?
  3. 如果是这样,我仍然可以不使用而获得尾递归收益loop吗?

zer*_*kms 5

只是一个接一个地回答您的问题:

  1. loop允许您接受并传递任意参数。如果没有,loop您将只能只接受函数接受的内容。这会导致堆很小的辅助功能

  2. 是的,有点

  3. 它必须是一个尾部调用,并且受编译器的约束


Thu*_*ail 5

  1. 从来没有任何需要使用loop. 您始终可以将其替换为对匿名表单的调用fn
  2. 是的。loop充当 alet兼作 的递归点 recur。如果 aloop没有捕获recurs,则可以用 a 替换它 let,反之亦然。
  3. 是的。它recur实现了尾递归(并且仅尾递归),无论它递归到 aloop还是fn表单。

为了说明(1),您可以替换loop示例2中的形式

  (loop [cnt num]
    (if (> cnt 10)
      cnt
      (recur (+ cnt 1))))
Run Code Online (Sandbox Code Playgroud)

... 和

((fn [cnt] (if (> cnt 10) cnt (recur (+ cnt 1)))) num)
Run Code Online (Sandbox Code Playgroud)

...如您所见,它创建并调用匿名函数。

您甚至可以编写loop为进行此转换的宏:

(defmacro loop [bindings & body]
  (let [[names values] (apply map vector (partition 2 bindings))]
    `((fn ~names ~@body) ~@values)))


(loop [i 10, ans 0]
  (case i
    0 ans
    (recur (dec i) (+ ans i))))
; 55
Run Code Online (Sandbox Code Playgroud)

这可能比正确的慢clojure.core/loop