【一人 bugs.ruby Advent Calendar 2020】[Bug #17030] Enumerable#grep{_v} should be optimized for Regexp【4日目】
一人 bugs.ruby Advent Calendar 2020 4日目の記事になります。
[Bug #17030] Enumerable#grep{_v} should be optimized for Regexp
ary.select { |e| e.match?(reg) }
と比較して ary.grep(reg)
の方が遅いので最適化しよう、という内容のチケットになります。
これは grep
で比較する際に #===
を使って比較しているのが原因です。
Regexp#===
で比較した場合に『最後に比較を行った正規表現マッチの情報』として MatchData
を生成しています。
/\d+/ === "homu1234" # 最後に比較した正規表現を MatchData として返す matchdata = Regexp.last_match pp matchdata # => #<MatchData "1234"> # 比較した文字列を返す pp matchdata.string # => "homu1234" # 比較した正規表現を返す pp matchdata.regexp # => /\d+/ # 実際にマッチした文字列 pp matchdata.to_s # => "1234"
今回の問題のボトルネックはこの MatchData
を生成している箇所で /\d+/.match? "homu1234"
だとこの MatchData
は生成されないのでその分早くなっています。
/\d+/.match? "homu1234" matchdata = Regexp.last_match # => nil ary = ["homu", "mami", "mado"] # こっちは MatchData は生成されない ary.select { |e| e.match?(/.*/) } p Regexp.last_match # => nil # こっちは MatchData が生成される ary.grep(/.*/) p Regexp.last_match # => #<MatchData "mado">
このチケットではこの『 MatchData
を生成するべきかどうか』の点を主に議論されています。
単純に #grep
や #===
で MatchData
を生成しないようにすると非互換な変更になってしまいなかなかに難儀している模様。
そこで提案されたのが /hoge/f
みたいに正規表現リテラルに f
オプションを追加し、それで制御できるようにすればいいのでは?という提案です。
このオプションを利用して
/hoge/
=>Regexp#===
で判定する/hoge/f
=>Regexp#match?
で判定する
のように用途によって使い分けるという感じです。
こんな感じで正規表現リテラルで制御することによって #grep
だけではなくて #any?
や #all?
などでも流用することができます。
正規表現リテラル側で制御する発想はなかったのでこの対応はなかなかかしこいなあ、と思いました。
現時点だとこの議論はストップしていますが気になる方がいれば何かしらコメントしてみるといいと思います。