Hakusan mafiaアドベントカレンダーの13日目の記事を書かせてもらいました、長谷川です。マフィアハウスなどで会った時はよろしくお願いします!
さて、今回のテーマは正規表現としました。バリバリに使っている人もいる一方であんまり使わないという人の方が多いと思うので、そういった方に目を通して欲しいです。
#はじめに
メタプログラミングRubyの3章を読んでいた時に急に正規表現が出てきたので、その意味を読解してみました。
#問題の箇所
Hashieというgemについての記述の時に、その正規表現は出てきました。
module Hashie
class Mash < Hashie::Hash
def method_missing(method_name, *args, &blk)
return self.[](method_name, &blk) if key?(method_name)
match = method_name.to_s.match(/(.*?)([?=!]?)$/)
case match[2]
when "="
self[match[1]] = args.first
(略)
end
end
(略)
end
end
/(.*?)([?=!]?)$/
これについて詳しく見ていこうと思います。
#分解してそれぞれチェック
##1, ()
正規表現では、()で囲った部分をグルーピングすることができます。まあこんなことは言わずもがなだとは思うのですが、実は正規表現においては、もう一つ別の機能があります。それは、()の対になっているグループは単なるグループではなくキャプチャを伴っており、それぞれに番号が割り振られるということです。
番号は左から見ていった時の()の順番でつけられていきます。つまり、(.*?)がグループ1で、([?=!]?)がグループ2です。これらのグループは、\1や\2で後方参照できたり、置き換えができたりもします。
今回は、下の行で
case match[2]
や、
self match[1] = args.first
という感じで、番号を指定してグループを参照しています。
##2, (.*?)
意味は、0文字以上の任意の文字列です。
「.」で改行を除く任意の一文字を表し、「*」で「.」の0回以上の繰り返しをマッチさせています。
「?」は、「*」や「+」のような量指定子の後につけることで、最短でマッチするような動きをさせます。いわゆる控え目な量指定子です。
この「?」をつけなかった場合、貪欲であるアスタリスクによって、マッチに必要な分以上のテキストを最初に取ってきてしまい、バックトラックの処理に時間がかかってしまうということが起きたりします。控えめって大切ですね。
この表現については参考までに[正規表現] .*?は最短マッチではないという記事も見てみてください。
##3, ([?=!]?)
「?」か「=」か「!」のどれかに1回マッチする、もしくはどれともマッチしないという意味です。
[]を使った記法は、文字クラスと呼ばれます。作用としては、[]の中に文字を一つ、あるいは複数指定した時に、指定した文字のいずれかとマッチするようにします。つまり、今回のケースでいえば「?=!」のいずれかとマッチするということです。
[?=!]ってなんだか意味ありげですが、文字クラスの中で特別な機能を持つのは、, ^, -, ] の4字だけなので、特別深い意味はありません。
末尾の「?」は、直前の正規表現の0回もしくは1回に一致させます。つまり、[?=!]が0回、または1回出てきた時にマッチします。
##4, $
直前にある正規表現マッチと、行末とを一致させるアンカーです。つまり、直前の正規表現グループ、([?=!]?)を、対象テキストの末尾とマッチさせます。
#まとめ
method_nameにmatchメソッドを当てていることからも分かるように、この正規表現には、メソッド名をマッチさせています。
メソッド名の末尾に=や?、!がついている場合にも対応できるようになっていますね。
また、正規表現を遊びながら勉強できるRegex Crosswordというクロスワードもあるので、興味あったら遊んでみてください!