最近の好きなメソッド
今更ながら、injectから、Symbol.to_procが急浮上しております。
あと、最近やっとsort_byなんてメソッドがあることに気付きました。気付いたきっかけというのが、この間の松江ruby会議のUstで行われていたパネラーによるコミット祭りで"sort_by_bangコミットしちゃおうかな"発言が飛び交っていたので気付きました。ちなみに、Rubyのソースでhoge_fuga_bangというのは、大抵破壊的なメソッドの実装のときにつかわれております。というわけで、Rubyのさきっちょにはsort_by!というメソッドができているはずです。
以前から、ログファイルを解析するときに、ログ1行分を表すクラスを作って、それぞれを配列にいれてソートして眺めるときに<=>をオーバーライドするのがめんどくさいなぁと思っていたんですが、1.9なら(1.8.7もですが)こうかけることにきづいた
class Hoge; attr_accessor :i;end (1..10).{|e| i=Hoge.new; i.i=e}.sort_by(&:i)
で、sort_byの説明をみると、こうかいてあった instance method Enumerable#sort_by
Enumerable#sort と比較して sort_by が優れている点として、 比較条件が複雑な場合の速度が挙げられます。 sort_by を使わない 以下の例では比較を行う度に downcase が実行されます。従って downcase の実行速度が遅ければ sort の速度が致命的に低下します。
というわけで、単純に Fixnum のインスタンス変数を文字列にしてから比較するやりかたと、そのまま比較するやりかたで時間をとってみた
#! /opt/local/bin/ruby1.9 #require 'profiler' class Hoge def initialize(i) @i = i end def i() @i.to_s end def <=>(o) i <=> o.i end end def prof_sort(ary) puts "sort" #Profiler__.start_profile s = Time.now ary.sort{|a,b| a.i <=> b.i} e = Time.now p (e - s) #Profiler__.print_profile(STDOUT) puts "sort_by Symbol.to_proc" #Profiler__.start_profile s = Time.now ary.sort_by(&:i) e = Time.now p (e - s) #Profiler__.print_profile(STDOUT) puts "sort_by" #Profiler__.start_profile s = Time.now ary.sort_by{|e| e.i} e = Time.now p (e - s) #Profiler__.print_profile(STDOUT) puts "<=> override" #Profiler__.start_profile s = Time.now ary.sort e = Time.now p (e - s) #Profiler__.print_profile(STDOUT) end ary = (1...1000000).map{|e| Hoge.new e} puts "method i is to_s" prof_sort ary class Hoge def i() @i end end puts "method i is fixnum" prof_sort ary
Rubyのaryはqsortで実装されているので、Rangeクラスから作った配列をソートなんてすると、簡単に最悪ケースにはまってくれる。で、実行結果。
takkanm% ruby1.9 sort_prof.rb method i is to_s sort 27.680841 sort_by Symbol.to_proc 3.323916 sort_by 3.3218 <=> override 25.805198 method i is fixnum sort 0.540341 sort_by Symbol.to_proc 0.457979 sort_by 0.589265 <=> override 0.54745
普通にfixnumで比較すると変化はないみたいですけど、そこで変換を行ったりするとかなり遅くなりますね。
念のため、プロファイラmodule Profiler__もとってみようと思ったけど、全然一つめのメソッド自体が返ってこない。なんだこれ。