何时使用纯递归以及何时使用循环/重复?

时间:2013-01-11 05:57:12

标签: 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]
  (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)))

3 个答案:

答案 0 :(得分:9)

需要考虑的几个因素:

  • loop / recur不占用堆栈空间 - 所以如果你要进行深层嵌套递归,那么它是正确的选择,否则可能导致StackOverflowError
  • loop / recur更快 - 它是Clojure中最有效的构造之一,正确完成它应该与Java代码中等效循环的速度匹配
  • 正常递归更具惯用性 - 平均而言,它往往会为您提供更清晰,功能更强大的代码,而loop / recur往往会让您更倾向于强制性的迭代风格
  • loop / recur有更多的限制 - 你只能在尾部位置重复,你不能在两个不同的函数之间做相互递归,等等。有时它根本不可能做出循环/重复工作,在其他时候你可能需要扭曲代码才能这样做。

答案 1 :(得分:2)

使用lazy-seq / lazy-cons机制的唯一原因是生成延迟序列。如果您不需要它们,那么毫无疑问会使用loop / recur

答案 2 :(得分:1)

首先编写函数时使用普通递归。如果可以的话,一旦你完成所有工作,然后将其更改为重复。

TCO的一个问题是,如果你不推测你的递归,你会得到一个无限的外观。没有,你的代码很好地崩溃堆栈溢出,这是你想要的。当我第一次听到它时,我不喜欢复发的想法 - 大多数优化都应该发生 - 但能够将其关闭是很好的。