見出し画像

「プロを目指す人のためのRuby入門」読書メモ #8 【正規表現とループ】

正規表現は前回少し触れました。

今回は正規表現を使ってPaiza Challengeの問題が解けたよーという話。解いた問題は大まかに以下のような内容でした。

複数行の文字列において、各行で特定の文字列が含まれるかを検証する。その際に1文字の揺らぎは許容する。

具体的には

# 含まれているかを判定する文字列
meat

# 判定対象の文字列(入力)
applewaterautumn
lookbookmeatrun
meazthavegood

# 各行毎に判定するので、期待される出力は上から
not include
include
include

といった問題でした。文字の判定なので正規表現だな、とはすぐに分かったのですが1文字の揺らぎをどのように表現したらよいかで少し詰まりました。そこで思いついた方法が、判定する文字列(上記の場合meat)を2つに分割します。その分割の割合を変化させながらループを回し、どれか一つでも該当すればincludeと判定する。というものでした。

コードは以下のような感じ。

# words_num : 行数
words_num.times do |count|

    # check_word : 入力された文字列
   check_word = gets.chomp
   
    # target_word : 本エントリではmeat
   if check_word.include?(target_word)
       # 完全一致か判定
       puts "include"
       
   else
       # 1文字の揺らぎで収まるか判定
       # buf_try_num : 分割して検証する試行回数
       buf_try_num = target_word.length - 1
       
       # buf_check_counter : 1文字の揺らぎで収まる場合、1にすることで出力を制御
       buf_check_counter = 0
       
       buf_try_num.times do |counter|
           # buf_before_word : 分割された文字列の前半部分
           # buf_after_word  : 後半部分
           buf_before_word = target_word[0, counter + 1]
           buf_after_word = target_word[counter + 1, target_word.length - (counter + 1)]
           
           # ここで正規表現を用いて検証
           # 式展開をすることで変数を利用する           
           if check_word.match(/#{buf_before_word}.#{buf_after_word}/)
               buf_check_counter += 1
           end
       end
       
       if buf_check_counter > 0
           puts "include"
       else
           puts "not include"
       end
       
   end
end

このプログラムのキモは

check_word.match(/#{buf_before_word}.#{buf_after_word}/)

だと思っています。ループで使用するには変数を用いる必要があったのですが、その際には式展開が出来ないとうまくいきません。調べてみると//内の式展開は有効とのことでしたので任意の1文字を表す.(ドット)を挟むことで1文字の揺らぎを表現することが出来ました。

ちょうど学習だった正規化と自分で思いついたアルゴリズムを組み合わせて解けたので、ただ1問解けた以上の収穫が得られたかと思います。

この記事が気に入ったらサポートをしてみませんか?