【问题标题】:How to use tail recursion correctly?如何正确使用尾递归?
【发布时间】:2017-02-21 23:09:26
【问题描述】:

我正在尝试从https://github.com/lspector/gp/blob/master/src/gp/evolvefn_zip.clj重写这段代码
使用递归:

(defn random-code [depth]
  (if (or (zero? depth)
          (zero? (rand-int 2)))
    (random-terminal)
    (let [f (random-function)]
      (cons f (repeatedly (get function-table f)
                          #(random-code (dec depth)))))))

问题是,我完全不知道该怎么做。
我唯一能想到的是这样的:

(defn random-code [depth]
  (loop [d depth t 0 c []]
    (if (or (zero? depth)
            (zero? (rand-int 2)))
      (if (= t 0)
        (conj c random-terminal)
        (recur depth (dec t) (conj c (random-terminal))))
      (let [f (random-function)]
        (if (= t 0)
          (recur (dec depth) (function-table f) (conj c f))
          (recur depth (dec t) (conj c f)))))))

这不是一段有效的代码,只是为了展示我尝试解决它的方式,它只会变得越来越复杂。
有没有更好的方法将普通递归转换为clojure中的尾递归?

【问题讨论】:

  • 是的,解决方案很可能像您的代码一样复杂。您构建树而不是序列的原因。如果您构建一个序列,您通常会添加一个累加器参数,它会替换代码中的 conj。在您的情况下,所有功能都是具有 2 个子节点的节点,它不起作用,您不能将 acc 发送到两个子节点。为什么要尾递归?只有当你想生成一个深度大于 Java 堆栈的树时,你才需要这个。 Java 拥有 1000 帧深的堆栈没有问题。尾递归可能会比你的版本慢。

标签: recursion clojure tail-recursion


【解决方案1】:

以下是比较递归算法和loop-recur 的 2 个示例:

(defn fact-recursion [n]
  (if (zero? n)
    1
    (* n (fact-recursion (dec n)))))

(defn fact-recur [n]
  (loop [count  n
         result 1]
    (if (pos? count)
      (recur (dec count) (* result count))
      result )))

(fact-recursion 5) => 120
(fact-recur 5) => 120

(defn rangy-recursive [n]
  (if (pos? n)
    (cons n (rangy-recursive (dec n)))
    [n]))

(defn rangy-recur [n]
  (loop [result []
         count  n]
    (if (pos? count)
      (recur (conj result count) (dec count))
      result)))

(rangy-recursive 5) => (5 4 3 2 1 0)
(rangy-recur 5) => [5 4 3 2 1]

基本区别在于,对于loop-recur,您需要第二个循环“变量”(此处命名为result)来累积算法的输出。对于普通递归,调用堆栈会累积中间结果。

【讨论】:

    猜你喜欢
    • 2014-09-13
    • 2015-09-20
    • 2016-03-21
    • 1970-01-01
    • 2011-07-01
    • 2019-09-30
    • 1970-01-01
    • 1970-01-01
    • 2018-09-03
    相关资源
    最近更新 更多