Haskellの末尾再帰を理解しようとしています。私はそれが何であるか、そしてそれがどのように機能するかを理解していると思いますが、私は物事を台無しにしないことを確認したいと思います。
「標準」の階乗の定義は次のとおりです。
factorial 1 = 1
factorial k = k * factorial (k-1)
たとえば、実行しているときfactorial 3
、私の関数はそれ自体を3回呼び出します(与えるか取るか)。スタックオーバーフローが発生する可能性があるため、階乗99999999を計算したい場合、これは問題を引き起こす可能性があります。到達した後factorial 1 = 1
、スタックに「戻って」すべての値を乗算する必要があるため、6つの操作があります(関数自体を呼び出すための3つと、値を乗算するための3つ)。
ここで、別の可能な階乗実装を示します。
factorial 1 c = c
factorial k c = factorial (k-1) (c*k)
これも再帰的です。自分自身を3回呼び出します。しかし、関数の引数としてすでに結果を渡しているので、すべての結果の乗算を計算するために「戻ってくる」必要があるという問題はありません。
これは、私が理解していることですが、末尾再帰についてです。今では、最初のものよりも少し良いように見えますが、それでもスタックオーバーフローを簡単に発生させることができます。Haskellのコンパイラは、末尾再帰関数を舞台裏でforループに変換すると聞いています。それが末尾再帰関数を実行することで報われる理由だと思いますか?
それが理由である場合、コンパイラがこの賢いトリックを実行しないのであれば、関数を末尾再帰にしようとする必要はまったくありません-私は正しいですか?たとえば、理論的にはC#コンパイラは末尾再帰関数を検出してループに変換できますが、現在はそれを行わないことを私は知っています(少なくとも私が聞いたことはそうです)。したがって、関数を末尾再帰にすることには、今日ではまったく意味がありません。それですか?
ありがとう!