Tidak, Ruby tidak menjalankan TCO. Namun, itu juga tidak melakukan TCO.
Spesifikasi Bahasa Ruby tidak menjelaskan apapun tentang TCO. Ia tidak mengatakan Anda harus melakukannya, tetapi juga tidak mengatakan Anda tidak dapat melakukannya. Anda tidak bisa mengandalkannya .
Ini tidak seperti Skema, di mana Bahasa Spesifikasi mengharuskan bahwa semua Implementasi harus melakukan TCO. Tetapi ini juga tidak seperti Python, di mana Guido van Rossum telah membuatnya sangat jelas pada beberapa kesempatan (terakhir kali hanya beberapa hari yang lalu) bahwa Implementasi Python tidak boleh melakukan TCO.
Yukihiro Matsumoto bersimpati pada TCO, dia hanya tidak ingin memaksa semua Implementasi untuk mendukungnya. Sayangnya, ini berarti Anda tidak dapat mengandalkan TCO, atau jika Anda melakukannya, kode Anda tidak lagi dapat dibawa ke Implementasi Ruby lainnya.
Jadi, beberapa Implementasi Ruby melakukan TCO, tetapi sebagian besar tidak. YARV, misalnya, mendukung TCO, meskipun (untuk saat ini) Anda harus secara eksplisit menghapus komentar baris di kode sumber dan mengkompilasi ulang VM, untuk mengaktifkan TCO - di versi mendatang ini akan aktif secara default, setelah implementasi terbukti stabil. Mesin Virtual Parrot mendukung TCO secara native, oleh karena itu Cardinal dapat dengan mudah mendukungnya juga. CLR memiliki beberapa dukungan untuk TCO, yang berarti IronRuby dan Ruby.NET mungkin dapat melakukannya. Rubinius mungkin bisa melakukannya juga.
Tetapi JRuby dan XRuby tidak mendukung TCO, dan mereka mungkin tidak mendukung, kecuali JVM itu sendiri mendapatkan dukungan untuk TCO. Masalahnya adalah ini: jika Anda ingin memiliki implementasi yang cepat, dan integrasi yang cepat dan mulus dengan Java, maka Anda harus kompatibel-stack dengan Java dan menggunakan stack JVM sebanyak mungkin. Anda dapat dengan mudah mengimplementasikan TCO dengan trampolin atau gaya penerusan lanjutan eksplisit, tetapi kemudian Anda tidak lagi menggunakan tumpukan JVM, yang berarti bahwa setiap kali Anda ingin memanggil ke Java atau memanggil dari Java ke Ruby, Anda harus melakukan beberapa jenis konversi, yang lambat. Jadi, XRuby dan JRuby memilih untuk menggunakan kecepatan dan integrasi Java melalui TCO dan kelanjutan (yang pada dasarnya memiliki masalah yang sama).
Ini berlaku untuk semua implementasi Ruby yang ingin berintegrasi erat dengan beberapa platform host yang tidak mendukung TCO secara native. Misalnya, saya kira MacRuby akan mengalami masalah yang sama.
Pembaruan: Berikut penjelasan bagus tentang TCO di Ruby: http://nithinbekal.com/posts/ruby-tco/
Pembaruan: Anda mungkin juga ingin melihat permata tco_method : http://blog.tdg5.com/introducing-the-tco_method-gem/
Di Ruby MRI (1.9, 2.0 dan 2.1) Anda dapat mengaktifkan TCO dengan:
RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false }
Ada usulan untuk mengaktifkan TCO secara default di Ruby 2.0. Ini juga menjelaskan beberapa masalah yang menyertainya: Pengoptimalan panggilan tail: aktifkan secara default ?.
Kutipan singkat dari tautan:
def fact(n) if n < 2 1 else n * fact(n-1) end end
def fact(n, r) if n < 2 r else fact(n-1, n*r) end end
sumber
Itu dapat memiliki tetapi tidak dijamin untuk:
https://bugs.ruby-lang.org/issues/1256
sumber
TCO juga dapat dikompilasi dengan mengubah beberapa variabel di vm_opts.h sebelum mengkompilasi: 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
sumber
Ini dibangun di atas jawaban Jörg dan Ernest. Pada dasarnya itu tergantung pada implementasi.
Saya tidak bisa mendapatkan jawaban Ernest untuk mengerjakan MRI, tetapi itu bisa dilakukan. Saya menemukan contoh ini yang berfungsi untuk MRI 1.9 hingga 2.1. Ini akan mencetak angka yang sangat besar. Jika Anda tidak menyetel opsi TCO ke true, Anda akan mendapatkan kesalahan "tumpukan terlalu dalam".
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
sumber