自分の興味の赴くままにIT技術系のネタを取りとめもなくメモっています。
Ruby言語やLinuxのネタが多いです。
Ruby言語やLinuxのネタが多いです。
September 05, 2006 [おもひで]
■ [Ruby] Module.nesting
例えば、Foo::Bar::Baz というモジュールがあった場合に、[Foo::Bar::Baz, Foo::Bar, Foo]という感じに外へ外へと返してくれるメソッドはないかなぁ、と思ってリファレンスマニュアルを探したら、Module.nestingを発見。そういえば、そんなメソッドもあったなぁ、と思って試してみた・・・。んだけど、2番目の戻り値が想定外だった。同じじゃないのか。ふーむ。
$ cat test.rb
module Foo
module Bar
module Baz
p Module.nesting
end
end
end
module Foo::Bar::Baz
p Module.nesting
end
$ ruby test.rb
[Foo::Bar::Baz, Foo::Bar, Foo]
[Foo::Bar::Baz]
$ ruby -v
ruby 1.8.4 (2005-12-24) [i686-linux]
■ [Ruby] Module.nesting(2)
injectを使って、自前で実装してみた。split(/::/)とせずにREG_NESTに代入して使い回すのは高速化の第一歩だよねー(後で追記:これ、うそ。下の方で検証結果あり)。
$ cat test.rb
REG_NEST = /::/
def nesting(klass)
klass.name.split(REG_NEST).inject([]) {|ret, v|
if ret.size == 0
ret << eval(v)
else
ret << eval("#{ret.last}::#{v}")
end
}.reverse
end
module Foo
module Bar
module Baz; end
end
end
require 'benchmark'
Benchmark.bm(9){ |x|
x.report("nesting"){
10000.times{
nesting(Foo::Bar::Baz)
nesting(Foo::Bar)
nesting(Foo)
}
}
}
$ ruby test.rb
user system total real
nesting 1.260000 0.220000 1.480000 ( 1.520556)
$ cat test.rb
REG_NEST = /::/
def nesting(klass)
ret = []
klass.name.split(REG_NEST).each do |v|
ret << ((ret.size == 0) ? eval(v) : ret.last.const_get(v))
end
ret.reverse
end
module Foo
module Bar
module Baz; end
end
end
require 'benchmark'
Benchmark.bm(9){ |x|
x.report("nesting"){
10000.times{
nesting(Foo::Bar::Baz)
nesting(Foo::Bar)
nesting(Foo)
}
}
}
$ ruby test.rb
user system total real
nesting 0.730000 0.160000 0.890000 ( 0.939074)
$ cat test.rb
REG_NEST = /::/
def nesting(klass)
ret = []
ary = klass.name.split(REG_NEST)
while(v = ary.shift)
ret.unshift(((ret.size == 0) ? eval(v) : ret[0].const_get(v)))
end
ret
end
module Foo
module Bar
module Baz; end
end
end
require 'benchmark'
Benchmark.bm(9){ |x|
x.report("nesting"){
10000.times{
nesting(Foo::Bar::Baz)
nesting(Foo::Bar)
nesting(Foo)
}
}
}
$ ruby test.rb
user system total real
nesting6 0.680000 0.070000 0.750000 ( 0.773468)
■ もっと速い方法ある?
■ [Ruby] 正規表現オブジェクトを定数で持つと高速化になる?
「split(/::/)とせずにREG_NESTに代入して使い回すのは高速化の第一歩」って書いたけど、これはホントか?と思ってちょっとやってみたらほとんど関係なかった。遅くなるときすらある。そういうものか。
$ cat test3.rb
require 'benchmark'
REGEXP = /::/
Benchmark.bm(8){ |x|
x.report("regexp1"){100000.times{"ABC::DEF".split(/::/)}}
x.report("regexp2"){100000.times{"ABC::DEF".split(REGEXP)}}
}
$ ruby test3.rb
user system total real
regexp1 0.690000 0.070000 0.760000 ( 0.790722)
regexp2 0.710000 0.070000 0.780000 ( 0.805602)
これは勉強になった。
$ cat test.rb
def nesting(klass)
ret = []
ary = klass.name.split(/::/)
while(v = ary.shift)
ret.unshift(((ret.size == 0) ? eval(v) : ret[0].const_get(v)))
end
ret
end
module Foo
module Bar
module Baz; end
end
end
require 'benchmark'
Benchmark.bm(9){ |x|
x.report("nesting"){
10000.times{
nesting(Foo::Bar::Baz)
nesting(Foo::Bar)
nesting(Foo)
}
}
}
$ ruby test.rb
user system total real
nesting 0.650000 0.070000 0.720000 ( 0.746397)
お、微妙に速い。
■ [Ruby] if文と三項演算子はどっちが速い?
こちらも心配になったので計ってみた。Ruby的には条件演算子と言うべきなのかな。
$ cat test4.rb
require 'benchmark'
Benchmark.bm(3){ |x|
x.report("if"){100000.times{if true; a = 1 else; a = 2;end}}
x.report("?:"){100000.times{a = true ? 1 : 2}}
}
$ ruby test4.rb
user system total real
if 0.090000 0.060000 0.150000 ( 0.166605)
?: 0.060000 0.090000 0.150000 ( 0.163028)
ちょっと三項演算子の方が速いかな。何回かやるとifの方が速くなる場合もあるけど、平均取ると三項演算子の方が速いっぽい。
$ cat test5.rb
require 'benchmark'
Benchmark.bm(8){ |x|
x.report("each"){a = []; 10000.times{(0..100).each {|v| a << v}}}
x.report("inject"){10000.times{(0..100).inject([]){|a, v| a << v}}}
}
$ ruby test5.rb
user system total real
each 1.380000 1.180000 2.560000 ( 2.593192)
inject 11.740000 1.820000 13.560000 ( 13.631210)
[ツッコミを入れる]
