RubyとStackサイズ

再帰段数は以下のコードで測りました。

puts "#{RUBY_PLATFORM} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"

i = [0]
def recursive(i)
  i[0] += 1
  recursive(i)
end
begin
  recursive(i)
rescue Exception => e
  p e
  puts "recursive size: #{i[0]}"
end

結果

$ jruby1.0 recursive.rb
java 1.8.5 (2007-05-31)
#<SystemStackError: stack level too deep>
recursive size: 934
$ ruby recursive.rb
i486-linux 1.8.6 (2007-03-13)
#<SystemStackError: stack level too deep>
recursive size: 4109
$ ruby1.9 recursive.rb
i486-linux 1.9.0 (2007-06-06)
#<SystemStackError: stack level too deep>
recursive size: 7265
$

どれもdebianパッケージのもので実行してますが、どれもサイズは少ないよなあ。ループだと10000なんて余裕で越すわけですし。

特に構文に仕込むRubyメタプログラミングのようなことをした場合、このサイズが1/2にも1/3にもなりますから。現状はなかなか汎用の処理に対しては、Rubyメタプログラミングスタイルはとりにくかったりする。ただし、こういうタイプのコードの場合、結局は最後に(ユーザー定義の)ブロックを呼ぶ末尾呼び出しになりやすいから、このへんは末尾呼び出し対応してたらうれしいのになあ、というのが、pattern matchingを実装していて思ったこと。

もう一個:

puts "#{RUBY_PLATFORM} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"

i = [0]
class Recursive
  def initialize(i)
    i[0] += 1
    Recursive.new(i)
  end
end
begin
  Recursive.new(i)
rescue Exception => e
  p e
  puts "recursive size: #{i[0]}"
end

結果

$ jruby1.0 recursive2.rb
java 1.8.5 (2007-05-31)
#<SystemStackError: stack level too deep>
recursive size: 649
$ ruby recursive2.rb
i486-linux 1.8.6 (2007-03-13)
#<SystemStackError: stack level too deep>
recursive size: 2824
$ ruby1.9 recursive2.rb
i486-linux 1.9.0 (2007-06-06)
#<SystemStackError: stack level too deep>
recursive size: 2564
$

もう一個、Rubyメタプログラミングで頻出のmethod_missingを使う場合

puts "#{RUBY_PLATFORM} #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"

i = [0]
class Recursive
  def method_missing(name, i)
    i[0] += 1
    self.call(i)
  end
end
begin
  Recursive.new.call(i)
rescue Exception => e
  p e
  puts "recursive size: #{i[0]}"
end

結果

$ jruby1.0 recursive3.rb
java 1.8.5 (2007-05-31)
#<SystemStackError: stack level too deep>
recursive size: 774
$ ruby recursive3.rb
i486-linux 1.8.6 (2007-03-13)
#<SystemStackError: stack level too deep>
recursive size: 3660
$ ruby1.9 recursive3.rb
i486-linux 1.9.0 (2007-06-06)
#<SystemStackError: stack level too deep>
recursive size: 3632
$