Ruby 1.9 だと String#sub 等にハッシュを渡せる
String#sub などの置換メソッドを何度も渡すと効率が問題になってくる。かといってブロックを渡すと ブロック呼び出しコストが高い(特に 1.8 )ため遅くなることがある。そこで、置換パターンをハッシュで渡せるようになった。これなら効率も上がる。
TABLE = { 'a' => 'A', 'b' => 'B' } # 複数回の置換 "hagbx".gsub (/a/, 'A').gsub (/b/, 'B') # => "hAgBx" # ブロック置換 "hagbx".gsub (/[ab]/){|c| TABLE[c] } # => "hAgBx" # ハッシュ置換( Ruby 1.9 ) "hagbx".gsub (/[ab]/, TABLE) # => "hAgBx"
で、ベンチマーク。
Ruby 1.9 の String#inspect で JSON 形式への変換を高速化 - WebOS Goodies から拝借。
# -*- coding: euc-jp -*- def profile (label) time = Time.now yield puts "#{label} : #{Time.now - time}\n" end ESCAPE_CONVERSION = { '\x' => '\u00', '\a' => '\u0007', '\v' => '\u000B', '\e' => '\u001B' } fixture = <<EOS \x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f \x22\x5c EOS profile ('inspect を使わない') do 100000.times do str = fixture.to_s.encode ('UTF-8').gsub (/[^\x20-\x21\x23-\x5b\x5d-\xff]/n) do |chr| c = chr[0].ord if c != 0 && (index = "\"\\/\b\f\n\r\t".index (chr[0])) "\\" + '"\\/bfnrt'[index, 1] else sprintf ("\\u%04X", c) end end end end profile ('ブロックで置換') do 100000.times do str = fixture.to_s.encode ('UTF-8').inspect str.gsub! (/\\[xave]/u){|s| ESCAPE_CONVERSION[s] } end end profile ('複数の gsub で置換') do 100000.times do str = fixture.to_s.encode ('UTF-8').inspect str.gsub (/\\x/u, '\u00').gsub (/\\a/u, '\u0007').gsub (/\\v/u, '\u000B').gsub (/\\e/u, '\u001B') end end profile ('複数の gsub! で置換') do 100000.times do str = fixture.to_s.encode ('UTF-8').inspect str.gsub! (/\\x/u, '\u00') str.gsub! (/\\a/u, '\u0007') str.gsub! (/\\v/u, '\u000B') str.gsub! (/\\e/u, '\u001B') end end # 追加! profile ('ハッシュ引数で置換') do 100000.times do str = fixture.to_s.encode ('UTF-8').inspect str.gsub! (/\\[xave]/u, ESCAPE_CONVERSION) end end # >> inspect を使わない : 21.437445578 # >> ブロックで置換 : 7.768467606 # >> 複数の gsub で置換 : 10.30494176 # >> 複数の gsub! で置換 : 10.731268803 # >> ハッシュ引数で置換 : 7.144320215
「ブロックで置換」よりも「ハッシュ引数で置換」のほうが少しだけ速い。置換パターンが増えてくるとかなり差がつくのかな…
俺のマシンは Pentium4 2.66GHz なのだが、 Core 2 Duo 2GHz よりちょっとばっかし遅いくらいだった。意外…
ていうか、 inspect をこういう目的で使うのはどうかと思うんだよなぁ。 inspect ってデバッグ目的の出力だから Ruby の仕様にフォーマットが定められているわけじゃないし、いつ変更されるか分からない。ちょっとリスキーかも…