回答:
いいえ、RubyはTCOを実行しません。ただし、TCO も実行されません。
Ruby言語仕様はTCOについて何も述べていません。それはあなたがそれをしなければならないというわけではありませんが、あなたがそれを行うことができないとも言いません。それに頼ることはできません。
これは、言語仕様は、スキーム、とは違っている必要があること、すべての実装はしなければならない TCOを行います。ただし、Guido van RossumがPython実装で TCOを実行すべきでないことを複数回(前回は数日前)非常に明確にしたPythonとは異なります。
松本幸宏はTCOに同情しています。彼はすべての実装にTCOのサポートを強制したくありません。残念ながら、これはTCOに依存できないことを意味します。そうしないと、コードは他のRuby実装に移植できなくなります。
そのため、一部のRuby実装はTCOを実行しますが、ほとんどは実行しません。たとえば、YARVはTCOをサポートしていますが、(現時点では)TCOをアクティブにするには、ソースコードの行のコメントを明示的に解除してVMを再コンパイルする必要があります。将来のバージョンでは、実装が証明された後、デフォルトでオンになります安定した。Parrot Virtual MachineはTCOをネイティブでサポートしているため、Cardinalも非常に簡単にサポートできます。CLRはTCOをある程度サポートしています。つまり、IronRubyとRuby.NETはおそらくそれを実行できます。ルビニウスもおそらくそうすることができたでしょう。
ただし、JRubyとXRubyはTCOをサポートしていません。JVM自体がTCOのサポートを得ない限り、おそらくそれらはサポートしません。問題はこれです:高速な実装とJavaとの高速でシームレスな統合が必要な場合は、Javaとスタック互換で、JVMのスタックをできるだけ使用する必要があります。トランポリンや明示的な継続渡しスタイルを使用してTCOを非常に簡単に実装できますが、JVMスタックを使用しなくなりました。つまり、Javaを呼び出したり、JavaからRubyを呼び出したりするたびに、何らかの種類の処理を実行する必要があります。変換は遅いです。そのため、XRubyとJRubyは、TCOと継続(基本的に同じ問題)を超える速度とJava統合を採用することにしました。
これは、TCOをネイティブにサポートしていないホストプラットフォームと緊密に統合したいRubyのすべての実装に適用されます。たとえば、MacRubyでも同じ問題が発生すると思います。
更新: RubyでのTCOの良い説明は次のとおりです:http : //nithinbekal.com/posts/ruby-tco/
更新:tco_method gem も確認してください:http : //blog.tdg5.com/introducing-the-tco_method-gem/
Ruby MRI(1.9、2.0、および2.1)では、次のコマンドでTCOをオンにできます。
RubyVM::InstructionSequence.compile_option = {
:tailcall_optimization => true,
:trace_instruction => false
}
Ruby 2.0では、デフォルトでTCOをオンにするという提案がありました。また、それに伴ういくつかの問題についても説明します。末尾呼び出しの最適化:デフォルトで有効にしますか?。
リンクからの短い抜粋:
一般に、末尾再帰の最適化には別の最適化手法が含まれます-「呼び出し」から「ジャンプ」への変換。Rubyの世界では「再帰」の認識が難しいため、この最適化を適用するのは難しいと思います。
次の例。「else」句でのfact()メソッドの呼び出しは、「末尾呼び出し」ではありません。
def fact(n)
if n < 2
1
else
n * fact(n-1)
end
end
fact()メソッドで末尾呼び出しの最適化を使用する場合は、fact()メソッドを次のように変更する必要があります(継続渡しスタイル)。
def fact(n, r)
if n < 2
r
else
fact(n-1, n*r)
end
end
次のことが可能ですが、保証はされません。
TCOは、コンパイルする前にvm_opts.hのいくつかの変数を調整することでコンパイルすることもできます:https : //github.com/ruby/ruby/blob/trunk/vm_opts.h#L21
// vm_opts.h
#define OPT_TRACE_INSTRUCTION 0 // default 1
#define OPT_TAILCALL_OPTIMIZATION 1 // default 0
これは、ヨルクとアーネストの答えに基づいています。基本的には実装に依存します。
アーネストの答えをMRIで処理することはできませんでしたが、それは可能です。MRI 1.9〜2.1で機能するこの例を見つけました。これは非常に大きな数を出力するはずです。TCOオプションをtrueに設定しない場合、「スタックが深すぎる」エラーが発生するはずです。
source = <<-SOURCE
def fact n, acc = 1
if n.zero?
acc
else
fact n - 1, acc * n
end
end
fact 10000
SOURCE
i_seq = RubyVM::InstructionSequence.new source, nil, nil, nil,
tailcall_optimization: true, trace_instruction: false
#puts i_seq.disasm
begin
value = i_seq.eval
p value
rescue SystemStackError => e
p e
end