6

これは、スタック オーバーフローの問題について尋ねられたときに人々が抱えている問題の逆であることはわかっていますが、関数を作成して次のように呼び出すと、エラーが発生することはなく、アプリケーションは私のコアをすりつぶすだけです。 CPU を強制終了するまで:

let rec recursionTest x =
    recursionTest x

recursionTest 1

もちろん、これを変更できるので、実際には次のようになります。

let rec recursionTest (x: uint64) =
    recursionTest (x + 1UL)

recursionTest 0UL

このようにして、コードにブレークポイントを設定し、x の値が急速に上昇するのを見ることができますが、それでも問題はありません。F# は無限再帰を気にしませんか?

4

3 に答える 3

9

あなたのrecursionTest関数は末尾再帰的です。つまり、すべての再帰呼び出しは「末尾位置」、つまり関数の最後のアクションとして発生します。つまり、F# コンパイラは再帰呼び出し用に新しいスタック フレームを割り当てる必要がないため、スタック オーバーフローは発生しません。

末尾再帰は末尾呼び出しの特定のケースであり末尾呼び出しは他の関数ではなくそれ自体に対するものです。

于 2012-11-30T23:05:56.087 に答える
4

一般に、F# は、.NET が受け入れる tailcall 命令を発行します。

http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.tailcall(v=vs.95).aspx

あなたのようないくつかの特定の単純なケースでは、F# は再帰を使用するプログラムを、ループを使用する同等のプログラムに書き換えます。

于 2012-11-30T23:06:54.700 に答える
3

これは末尾呼び出しの最適化の例であるため、コンパイラはそれを単純なループに最適化しています。これを参照してください: https://devblogs.microsoft.com/fsharpteam/tail-calls-in-f/

次のようなことを試してください:

let rec recursionTest x =
    recursionTest x + recursionTest (x * 2)
于 2012-11-30T23:05:24.113 に答える