重複の可能性:
.net / C#が末尾再帰を排除しないのはなぜですか?
次のC#コードを使用します。
using System;
namespace TailTest
{
class MainClass
{
public static void Main (string[] args)
{
Counter(0);
}
static void Counter(int i)
{
Console.WriteLine(i);
if (i < int.MaxValue) Counter(++i);
}
}
}
C#コンパイラ(とにかく私のもの)は、Counterメソッドを次のCILにコンパイルします。
.method private static hidebysig default void Counter (int32 i) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void class [mscorlib]System.Console::WriteLine(int32)
IL_0006: ldarg.0
IL_0007: ldc.i4 2147483647
IL_000c: bge IL_0019
IL_0011: ldarg.0
IL_0012: ldc.i4.1
IL_0013: add
IL_0014: call void class TailTest.MainClass::Counter(int32)
IL_0019: ret
}
上記のコードの問題は、スタックオーバーフローが発生することです(私のハードウェアでは約i = 262000)。この問題を回避するために、一部の言語は、末尾呼び出しの除去または末尾呼び出しの最適化(TCO)と呼ばれるものを実行します。基本的に、再帰呼び出しをループに変えます。
私の理解では、.NET 4 JITの64ビット実装はTCOを実行し、上記のCILのようなコードのオーバーフローを回避します。ただし、32ビットJITはそうではありません。モノもそうではないようです。JIT(時間とリソースのプレッシャーにさらされている)がTCOを実行するのに対し、C#コンパイラーは実行しないのは興味深いことです。私の質問は、C#コンパイラ自体がTCOに対応していないのはなぜですか?
JITにTCOを実行するように指示するCIL命令があります。たとえば、C#コンパイラは代わりに次のCILを生成できます。
.method private static hidebysig default void Counter (int32 i) cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call void class [mscorlib]System.Console::WriteLine(int32)
IL_0006: ldarg.0
IL_0007: ldc.i4 2147483647
IL_000c: bge IL_001c
IL_0011: ldarg.0
IL_0012: ldc.i4.1
IL_0013: add
IL_0014: tail.
IL_0017: call void class TailTest.MainClass::Counter(int32)
IL_001c: ret
}
オリジナルとは異なり、このコードはオーバーフローせず、32ビットJIT(.NETとMonoの両方)でも完全に実行されます。魔法はtail.
CIL命令にあります。F#などのコンパイラは、この命令を含むCILを自動的に生成します。
だから私の質問は、C#コンパイラがこれを行わないという技術的な理由はありますか?
歴史的には、それだけの価値がなかったのかもしれません。のようなコードCounter()
は、慣用的なC#や.NETフレームワークでは一般的ではありませんでした。C#のTCOは、不要または時期尚早の最適化と簡単に見なすことができます。
LINQなどの導入により、C#とC#の両方の開発者がより機能的な方向に進んでいるようです。したがって、再帰を使用することがそれほど危険なことではなかったとしたら、それは素晴らしいことです。しかし、私の質問は実際にはもっと技術的なものです。
このようなTCOをC#にとって悪い考え(または危険な考え)にする何かが欠けています。それとも、正しく理解するのが特に難しいものはありますか?それが私が理解したいと思っていることです。何か洞察はありますか?
編集:素晴らしい情報をありがとう。この機能の欠如を批判したり、要求したりしていないことを明確にしたかっただけです。私は優先順位付けに関する合理性にはあまり興味がありません。私の好奇心は、これを困難、危険、または望ましくないものにする障害を認識または理解できない可能性があることです。
おそらく、別のコンテキストが会話に集中するのに役立ちます...
CLRに独自のC#のような言語を実装しようとしていたとしましょう。なぜ私は(機会費用以外に)「尾」の自動で透明な放出を含めないのでしょうか。必要に応じて指示?C#に非常によく似た言語でこの機能をサポートする際に、どのような課題に直面したり、どのような制約を導入したりしますか。
返信ありがとうございます(そして事前に)。