loop/recur 和 recur 本身有什么区别?

What is the difference between loop/recur and recur by itself?

我在 Clojure 文档中找不到这个问题的答案。我是 Clojure 的新手,您似乎可以用两种不同的方式使用 recur 并获得相同的结果。

示例 1:

(defn my-function [num]
  (if (> num 10)
    num
    (recur (+ num 1))))

示例 2:

(defn my-function [num]
  (loop [cnt num]
    (if (> cnt 10)
      cnt
      (recur (+ cnt 1)))))

据我所知,这两种形式似乎做同样的事情。我知道 recur 之所以好的一般是因为在适当的情况下,编译器可以将某种伪尾调用优化组合在一起,我真的很喜欢并且愿意尽可能多地使用它.所以这是我的问题:

  1. recur 似乎没有它时,使用 loop 有什么必要?
  2. 是否 loop 只是创建一个 "recursion scope" 有点像 let 创建迷你范围的方式?
  3. 如果是这样,我仍然可以在不使用 loop 的情况下获得尾递归的好处吗?

只为一一解答您的问题:

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

  2. 是的,有点像

  3. 一定是尾调用,受编译器约束

  1. 从不 需要使用 loop。你可以总是替换它 调用匿名 fn 表单。
  2. 是的。 loop 作为 let 兼作递归点 recur。如果 loop 没有捕获到 recur,您可以将其替换为 let,反之亦然。
  3. 是的。 recur 实现了尾递归(而且只有尾递归 递归),无论它递归为 loop 还是 fn 形式。

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

  (loop [cnt num]
    (if (> cnt 10)
      cnt
      (recur (+ cnt 1))))

...与

((fn [cnt] (if (> cnt 10) cnt (recur (+ cnt 1)))) num)

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

您甚至可以将 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

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