From: Shinya Kawaji Date: 2003-03-13T16:16:08+09:00 Subject: [ruby-list:37297] Re: 両方の式とも常に評価する論理和・論理積 かわじ、です。 > > class String > > def s_strip! > > tmp1 = gsub!(/\A\s+/,'') > > tmp2 = gsub!(/\s+\z/,'') > > (tmp1 or tmp2) ? self : nil > > end > > end > > > > とローカル変数を使うか、 > > これが分かり易くて良いのではありませんかね? [snip] > > スクリプトを記述する際の文字数を減らすとか、実行速度を最大にするとかで > あればともかく、誰が読んでも意図が伝わる(伝わり易い)書き方が好ましいん > じゃないかなぁ、と個人的には思います。 ありがとうございます。元々は、もっと短くなる記法があれば、それが結局、 もっと速くなることにつながると思っていたのですが、ちょっと違うようです。 わたなべさんの「正規表現を一つにまとめる」も含めて、ベンチマークを 取ってみました。(ありがとうございます>わたなべさん) (CPU: celeron400MHz, Memory: 64MBくらい) $ ruby -v ruby 1.6.7 (2002-03-01) [i386-openbsd3.1] $ cat tmp.rb require 'benchmark' class String # 正規表現を一つにまとめる def s_strip1! gsub!(/\A\s+|\s+\z/,'') end # 戻り値を考慮しない def s_strip2! gsub!(/\A\s+/,'') gsub!(/\s+\z/,'') end # ローカル変数を使用 def s_strip3! tmp1 = gsub!(/\A\s+/,'') tmp2 = gsub!(/\s+\z/,'') (tmp1 or tmp2) ? self : nil end # 補助関数を使用 def s_strip4! __get_result( gsub!(/\A\s+/,''), gsub!(/\s+\z/,'') ) end def __get_result(*args) args.compact.empty? ? nil : self end # !!を使用 def s_strip5! (!!gsub!(/\A\s+/,'') | !!gsub!(/\s+\z/,'')) ? self : nil end # findを使用 def s_strip6! [gsub!(/\A\s+/,''), gsub!(/\s+\z/,'')].find{|x| x} end # nil?を使用 def s_strip7! gsub!(/\A\s+/,'').nil? & gsub!(/\s+\z/,'').nil? ? nil : self end end TIMES = 50_000 TEXT = [ "abc", "abc \r\n", "\n\t abc", "\n\t abc \r\n", ] RESULT = 'abc' # 戻り値をチェック 1.upto(7){|i| TEXT.each_with_index{|text,j| source = text.dup result = source.send("s_strip#{i}!") raise unless source == RESULT unless i == 2 # 「戻り値を考慮しない」を除く if j == 0 # 変更無しは nil を返す raise unless result.nil? else raise unless result == RESULT end end } } Benchmark.bm(12){|x| 1.upto(7){|i| x.report("s_strip#{i}!:"){ TIMES.downto(1){ TEXT.each{|text| text.dup.send("s_strip#{i}!") } } } } } $ ruby tmp.rb user system total real s_strip1!: 11.440000 0.000000 11.440000 ( 11.438658) s_strip2!: 12.300000 0.000000 12.300000 ( 12.347814) s_strip3!: 11.250000 0.000000 11.250000 ( 11.250715) s_strip4!: 15.240000 0.000000 15.240000 ( 15.260732) s_strip5!: 12.700000 0.000000 12.700000 ( 12.697829) s_strip6!: 15.380000 0.000000 15.380000 ( 15.394424) s_strip7!: 12.880000 0.000000 12.880000 ( 12.902847) なぜか、「ローカル変数を使う」が最速です。 何回かやってみると、「正規表現をまとめる」が最速のこともありますが、 まさか「ローカル変数を使う」が「戻り値を考慮しない」よりも 速いとは思ってみませんでした。 (ベンチマークの取り方が間違っているんでしょうか?) 試しに、Ruby1.8.0 でもやってみました。 (アーキテクチャは上記と違って、PentiumIIIかIVの1.0GHzくらい) $ ruby -v ruby 1.8.0 (2003-02-16) [i686-linux] $ ruby tmp.rb user system total real s_strip1!: 7.840000 0.010000 7.850000 ( 7.847412) s_strip2!: 8.890000 0.000000 8.890000 ( 8.887431) s_strip3!: 7.950000 0.000000 7.950000 ( 7.957038) s_strip4!: 10.730000 0.010000 10.740000 ( 10.730664) s_strip5!: 9.090000 0.020000 9.110000 ( 9.112299) s_strip6!: 11.970000 0.000000 11.970000 ( 11.968175) s_strip7!: 9.320000 0.010000 9.330000 ( 9.335643) こちらは、「正規表現をまとめる」「ローカル変数を使う」の順です。 あくまでも現在の実装に限った話ですし、最速こそが全てではないのですが、 自分の思いこみで「これが速いはずだ」と思っていたのが、全然違うと いうことに気付きました。 ローカル変数を使う方法は、ソースの変更の際に、例えば内部の破壊的メソッド をもう一つ増やしたら、その結果を記録するローカル変数も増やさないと いけないのが嫌だなあ、と思っていたのですが、どれもそれほど速さに違いは ないですし、読みやすさやメンテナンスのしやすさの兼ね合わせをもうちょっと 考えることにします。 -- Shinya Kawaji