foo(...) + foo(...)
末尾再帰への最後の呼び出しとして「通常の」再帰を変換する一般的な方法があるかどうか疑問に思っていました。
例(スカラ):
def pascal(c: Int, r: Int): Int = {
if (c == 0 || c == r) 1
else pascal(c - 1, r - 1) + pascal(c, r - 1)
}
再帰関数を同等の末尾呼び出しに変換する関数型言語の一般的な解決策:
簡単な方法は、非末尾再帰関数をTrampoline
モナドでラップすることです。
def pascalM(c: Int, r: Int): Trampoline[Int] = {
if (c == 0 || c == r) Trampoline.done(1)
else for {
a <- Trampoline.suspend(pascal(c - 1, r - 1))
b <- Trampoline.suspend(pascal(c, r - 1))
} yield a + b
}
val pascal = pascalM(10, 5).run
そのため、パスカル関数は再帰関数ではなくなりました。ただし、Trampoline モナドは、実行する必要がある計算のネストされた構造です。最後に、run
ツリーのような構造をたどって解釈し、最後に基本ケースで値を返す末尾再帰関数です。
トランポリンに関する Rúnar Bjanarson の論文: Stackless Scala With Free Monads