tl;dr;
C# では、それ自体しか呼び出さず、有効な再帰終了条件を持つ遅延反復子関数がスタック オーバーフローを引き起こさないという保証はありますか?
詳細な質問:
原則として、TCO (Tail Call Optimization) 命令が C# コンパイラ (または JIT) によって生成されるという保証は得られないことを私は知っています。
この TCO の認識を考えると、コルーチンとしての性質のために遅延イテレータ関数 ( yield return
etc を使用) があるのではないかと考えています。再入可能性があるため、コルーチンの私の直感は、新しいフレームを作成する代わりに、関数から飛び出して親のフレームから次の関数にジャンプする機能が自然に見えるため、各末尾呼び出しがデフォルトで最適化されるということです。
これは C# での動作ですか、それとも C# イテレータ関数の再帰呼び出しは、親フレームに飛び出して新しいパラメータで再入力するのではなく、現在のフレームから新しいフレームを作成しますか?
例:
public static IEnumerable<IEnumerable<T>> GeneratePermutations<T>(this IEnumerable<T> choices, int numberToChoose)
{
if (numberToChoose == 1)
{
foreach (var choice in choices)
yield return new T[] { choice };
yield break;
}
var subPermutations = choices.SelectMany(choice =>
choices.Where(elem => !EqualityComparer<T>.Default.Equals(elem, choice))
.GeneratePermutations(numberToChoose - 1)
.Select(permutation => (new T[] { choice }).Concat(permutation)));
foreach (var perm in subPermutations)
yield return perm;
}
私の直感は、上記の例に基づいていますsubPermutations
。それは単に山積みの計算であり、それを反復するように呼び出されたように見えます。現在のフレームから、ヒープされた計算を新しいフレームに拡張します-再帰呼び出しが試行される前にそこにあったものに余分なスタックスペースはかかりません...
この直感はまったく根拠のないものかもしれません...