【问题标题】:convert recursive function to use tail recursion将递归函数转换为使用尾递归
【发布时间】:2012-11-06 06:22:43
【问题描述】:

假设我有这样的功能:

user=> (def m {10 5, 5 2, 2 1})
#'user/m
user=> (defn hierarchy [x] (when x (cons x (hierarchy (get m x)))))
#'user/hierarchy
user=> (hierarchy 10)
(10 5 2 1)
user=> 

显然这在这里很好,因为堆栈深度会很小。但是对于这种一般类型的问题,我正在构建一个我想要返回的列表,递归调用总是在 cons 调用中结束。如何将其转换为尾递归,以便我可以使用递归而不占用堆栈空间?

【问题讨论】:

    标签: clojure tail-recursion


    【解决方案1】:

    阅读累加器。

    在 Clojure 中,这个特定问题可以通过使用 lazy-seq 来解决。 lazy-seq 推迟计算,因此堆栈溢出(通常)不是问题。

    (defn hierarchy
      [x]
      (when x
        (lazy-seq
          (cons x (hierarchy (get m x))))))
    

    【讨论】:

    • 也许您可以添加 lazy-seq 将递归调用的计算推迟到函数的调用实例退出之后,因此递归调用实际上永远不会存在于同一个堆栈中。 “抓住头”可能仍然会耗尽内存,但不是因为堆栈溢出。
    • 当您将序列堆叠在序列上时,您仍然可能会遇到堆栈溢出。示例:(loop [x (seq some-input) s nil]) (if s (recur (next x) (concat (do-stuff (first x)) s)) s))。在实现返回的s 时,您会在concat 级联上启动一系列实现,这可能导致堆栈溢出。
    【解决方案2】:

    您可以在不使用递归的情况下优雅地解决这个问题:

    (defn hierarchy [x]
      (take-while identity (iterate m x)))
    

    【讨论】:

    • 你甚至可以去掉partial,直接使用m
    • 谢谢,我想知道是否有办法在没有显式递归的情况下做到这一点。
    【解决方案3】:

    第一个变种

    (defn hierarchy* [res x]
      (if-not x
        res
        (recur (conj res x) (get m x))))
    
    (defn hierarchy [x]
      (hierarchy* [] x))
    

    第二次

    (defn hierarchy [x]
      (loop [res []
             next-x x]
        (if-not next-x
          res
          (recur (conj res next-x) (get m next-x)))))
    

    【讨论】:

    • 对于第一个实现,您不需要定义分离函数hierarchy*。在 Clojure 中,可以为不同数量的参数 clojure.org/functional_programming 定义不同的行为。
    • 将此标记为正确。我感谢所有答案,但这最接近我试图理解的内容(即使在 clojure 中有其他可能更好的方法)。
    【解决方案4】:

    添加lazy-seq:

    (defn hierarchy [x] (when x (cons x (lazy-seq (hierarchy (get m x))))))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-06
      • 2019-09-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-27
      • 2021-12-16
      相关资源
      最近更新 更多