【发布时间】:2016-06-23 11:55:52
【问题描述】:
在阅读 Guido's reasoning for not adding tail recursion elimination to Python 时,我在 Haskell 中编造了这个几乎尾递归的例子:
triangle :: Int -> Int
triangle 0 = 0
triangle x = x + triangle (x - 1)
这当然不是尾调用,因为虽然递归调用本身是在“返回”中,但 x + 会阻止当前堆栈被重复用于递归调用。
但是,可以将其转换为尾递归代码(尽管相当丑陋和冗长):
triangle' :: Int -> Int
triangle' = innerTriangle 0
where innerTriangle acc 0 = acc
innerTriangle acc x = innerTriangle (acc + x) (x - 1)
这里的innerTriangle 是尾递归的,由triangle' 启动。虽然微不足道,但似乎这样的转换也适用于其他任务,例如构建列表(这里acc 可能只是正在构建的列表)。
当然,如果函数返回中没有递归调用,这似乎是不可能的:
someRecusiveAction :: Int -> Bool
someRecursiveAction x = case (someRecursiveAction (x * 2)) of
True -> someAction x
False -> someOtherAction x
但我只指“几乎尾”调用,其中递归调用是返回值的一部分但由于另一个函数应用程序包装它而不在尾位置(例如 x + 在 @ 987654331@ 上面的例子)。
这是否可以在功能环境中推广?势在必行的呢?是否可以将所有在其返回中具有递归调用的函数转换为在尾部位置返回的函数(即可以进行尾部调用优化的函数)?
别介意这些都不是在 Haskell 中计算三角形数的“最佳”方法,AFAIK 是 triangle x = sum [0..n]。该代码纯粹是这个问题的人为示例。
注意:我已经阅读了Are there problems that cannot be written using tail recursion?,所以我相当有信心我的问题的答案是肯定的。但是,答案提到了延续传递风格。除非我误解了 CPS,否则我转换后的triangle' 似乎仍然是直接风格。在这种情况下,我很好奇如何让这种转换以直接方式泛化。
【问题讨论】:
-
不能对所有函数进行转换,使其只有带有 CPS 转换的尾调用吗?
标签: recursion functional-programming theory tail-recursion compiler-theory