在閱讀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
但我只提到「幾乎尾巴」呼籲,那些在遞歸調用是返回值的一部分,但由於其他函數應用程序包裝它而不在尾部位置(例如上面triangle
示例中的x +
)。
這是泛化的功能上下文嗎?那麼一個必要的呢?所有在遞歸調用中的函數都可以被轉換成尾部位置返回的函數(即可以調用尾部優化的函數)?
不要緊,這些都不是計算Haskell中三角形數的「最佳」方法,AFAIK是triangle x = sum [0..n]
。該代碼純粹是這個問題的一個人爲的例子。
注:我讀過Are there problems that cannot be written using tail recursion?,所以我相當有信心我的問題的答案是肯定的。但是,答案提到了延續傳球的風格。除非我誤解CPS,否則我的轉換triangle'
仍然是直接風格。在這種情況下,我很好奇這種轉換是直接式的。
是不是所有的函數都能被轉換,使得它們只有帶CPS轉換的尾調用? – Claudiu