Ruby
正規表現

キャプチャ数が動的に変化する正規表現の具体例

この前たのしいRubyを買いまして、絶賛勉強中です。

問題演習中にはまったところがあったのでまとめておこうと思います。


アルファベットとハイフンから構成される文字列を、ハイフンで区切って単語部分についてcapitalizeする

splitすることで簡単にできます。


rubypractice_342.rb

def word_capitalize_split(str) # str = "ruby-php-Python-COBOL-JavaScript-javA"

nstr = []
result_split = ""
nstr = str.split(/-/) # ハイフンで分割し配列に格納
nstr.each do |word|
result_split << word.capitalize + "-" # 単語をCapitalizeしハイフンと一緒に文字列に入れる
end
return result_split.chop # "Ruby-Php-Python-Cobol-Javascript-Java"
end

しかし、この問題は正規表現クラスの章末問題でした。なので正規表現でも解いてみましょう。(実はもっと簡単にできたりしないかな・・・)


rubypractice_342.rb

def word_capitalize_regexp(str) # str = "ruby-php-Python-COBOL-JavaScript-javA"

count = 0
str.each_char do |n| # 単語の中のハイフンの数を数える
if n == "-"
count += 1
end
end

if count > 0
separator = "(.+)"
for i in 1..count
separator << "-(.+)" #ハイフンの数だけセパレーターに追加する
end

Regexp.new(separator) =~ str # ハイフン区切りで単語をキャプチャ
result = $1.capitalize # 一つ目のキャプチャした単語をCapitalize
# ex.1 ハマったところここから
for i in 2..count + 1
result << "-" + eval('$'+i.to_s).capitalize # 二つ目以降のキャプチャした単語をCapitalize
end
# ex.1 ここまで
return result # "Ruby-Php-Python-Cobol-Javascript-Java"
else
return str.capitalize
end
end


ここで、メソッドに渡される文字列strにはハイフンがいくつ入ってくるかわからないので、キャプチャする単語数も動的に変化する点が問題となりました。

例えば、strにくる単語は"ruby-JavaScript-javA"かもしれないし、"ruby-php-Python-COBOL-JavaScript-javA"かもしれないという感じです。separatorは"(.+)-(.+)-(.+)"や"(.+)-(.+)-(.+)-(.+)-(.+)-(.+)"でええやろ、となったのですが・・・


evalによるコード評価

最初はex1部分は下のように書いてありました。


rubypractice_342.rb

    for i in 2..count + 1  

result << "-" + "$#{i}".capitalize
end


しかし、この場合の結果は"ruby-php-Python" => "Ruby-$2-$3"となってしまいます。

どうやら、$2や$3が文字列オブジェクトではなくリテラルとして処理されてしまっているようです。

そこで、このリテラルがRubyのコードとして評価してもらえるよう、evalメソッドを使用します。


rubypractice_342.rb

    for i in 2..count + 1

result << "-" + eval('$'+i.to_s).capitalize # 二つ目以降のキャプチャした単語をCapitalize
end


これにより、文字列部分がプログラムとして実行され、想定どおりに結果が得られます。


参考

Rubyでメタプログラミングことはじめ