loop / recur和recur之间有什么区别?

时间:2015-11-20 05:04:12

标签: clojure

很抱歉,如果这是重复的话。如果是这样,我找不到原件。我也很难在Clojure文档中找到答案。无论如何,我是Clojure的新手,似乎你可以用两种不同的方式使用ids_list = range(1000000) ids_set = set(ids) def f(x): for i in x: pass %timeit f(ids_set) #1 loops, best of 3: 214 ms per loop %timeit f(ids_list) #1 loops, best of 3: 176 ms per loop 并且基本上得到相同的结果。

示例1:

recur

示例2:

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

据我所知,这两种形式似乎完全相同。我理解(defn my-function [num] (loop [cnt num] (if (> cnt 10) cnt (recur (+ cnt 1))))) 一般是好的原因是在正确的情况下,编译器可以将某种伪尾调用优化混合在一起,我非常喜欢并希望尽可能多地使用它。所以这是我的问题:

  1. recur在没有loop的情况下使用recur时有什么需要?
  2. loop只是创建一个"递归范围"有点像let如何创建迷你范围?
  3. 如果是这样,我是否仍然可以在不使用loop的情况下获得尾递归优势?

2 个答案:

答案 0 :(得分:4)

只是逐一回答你的问题:

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

  2. 是的,有点

  3. 它必须是尾调用,并且受编译器

  4. 的约束

答案 1 :(得分:2)

  1. 从不需要使用loop。您可以始终替换它 拨打匿名fn表单。
  2. 是。 loop充当let,可以作为递归点使用 recur。如果loop没有捕获recur,则可以将其替换为。{1}} let,反之亦然。
  3. 是。它是recur实现尾递归(并且只有尾部 递归),无论是递归到loop还是fn形式。
  4. 为了说明(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慢。