Recursion 何时使用纯递归,何时使用循环/递归?

Recursion 何时使用纯递归,何时使用循环/递归?,recursion,clojure,Recursion,Clojure,“纯递归”在这里是一个虚构的术语,请原谅 下面是两个使用两种不同递归方法的示例。使用一个或另一个的指导原则是什么 (defn take-while "Returns a lazy sequence of successive items from coll while (pred item) returns true. pred must be free of side-effects." {:added "1.0" :static true} [pred coll]

“纯递归”在这里是一个虚构的术语,请原谅

下面是两个使用两种不同递归方法的示例。使用一个或另一个的指导原则是什么

(defn take-while
  "Returns a lazy sequence of successive items from coll while
  (pred item) returns true. pred must be free of side-effects."
  {:added "1.0"
   :static true}
  [pred coll]
  (lazy-seq
   (when-let [s (seq coll)]
       (when (pred (first s))
         (cons (first s) (take-while pred (rest s)))))))

(defn take-last
  "Returns a seq of the last n items in coll.  Depending on the type
  of coll may be no better than linear time.  For vectors, see also subvec."
  {:added "1.1"
   :static true}
  [n coll]
  (loop [s (seq coll), lead (seq (drop n coll))]
    (if lead
      (recur (next s) (next lead))
      s)))

需要考虑的几个因素:

  • 循环/递归不会占用堆栈空间-因此,如果要执行深度嵌套的递归,则这是正确的选择,否则可能会导致
    堆栈溢出错误
  • 循环/重现速度更快-它是Clojure中最有效的构造之一,正确完成后,它应该与Java代码中的等效for循环速度相匹配
  • 普通递归更惯用——一般来说,它倾向于为您提供更清晰、更实用的代码,而循环/递归则倾向于将您推向命令式、迭代式的风格
  • 循环/递归有更多限制-您只能在尾部位置进行递归,不能在两个不同的函数之间进行相互递归,等等。有时循环/递归无法工作,有时可能需要扭曲代码才能工作

    • 使用
      lazy-seq
      /
      lazy-cons
      机制的唯一原因是生成惰性序列。如果您不需要它们,那么毫无疑问应该使用
      循环
      /
      递归

      当您首先编写函数时,请使用普通递归。然后,如果可以的话,将其更改为在所有操作都正常后重新出现


      TCO的一个问题是,如果你阻止递归,你会得到一个无限的外观。如果没有,代码会很好地崩溃,并出现堆栈溢出,这正是您想要的。当我第一次听说它的时候,我不喜欢它的出现——大多数的优化都应该发生——但是能够关闭它是很好的

      我真的不明白你在说什么。在第一个示例中,您根本无法选择循环/重复方式,因为
      take而
      不在尾部位置使用。有什么问题?循环/递归总是比函数递归调用更好,并且@mikera解释了原因。编译器不可能将take while中形式的惯用递归(递归是最后一个)转换为循环/递归形式(或它为循环/递归形式生成的代码)?@Hendekagon:在这种情况下,因为递归不在尾部位置。理论上,它可以在其他情况下使用,但我相信Rich Hickey的设计决策是不自动执行此操作-因此,如果您想要尾部递归,您必须使用
      recur