FizzBuzzを正規表現で解きたくなったので解いた
知ってる人が多いと思いますが念のため問題の内容を説明しておくと、入力された整数が3の倍数のときはFizz、5の倍数のときはBuzz、15の倍数のときはFizzBuzz、その他のときは数値をそのまま出力します
正規表現
というわけで書いた正規表現のコマンドを以下に書いておきます(seq 1 100
は入力の整数列)
seq 1 100 | perl -pe 's/^(\d*[05])$/$1Buzz/;s/^((?:[0369]|[147](?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147])|[258](?:[0369]|[258][0369]*[147])*(?:[147]|[258][0369]*[258]))+)(?=\D)/$1Fizz/;s/^\d+(?=B|F)//'
簡単に説明
5の倍数
^(\d*[05])$
末尾が0か5ならよいので判定は簡単です
3の倍数
(?:[0369]|[147](?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147])|[258](?:[0369]|[258][0369]*[147])*(?:[147]|[258][0369]*[258]))+
以下の記事の正規表現を参考にしてほぼそのまま使わせていただきました
d.hatena.ne.jp
上の記事を読んだほうがわかりやすいと思いますが、雑に説明しておきます
ある数の各桁の和が3の倍数であれば、その数は3の倍数であることが知られています
それぞれの数字は3で割ったあまりが0になる数[0369]
、1になる数[147]
、2になる数[258]
に分けることができます
3の倍数は、あまりが0の状態から変化して0の状態に戻るループとなる数字列の繰り返しとして考えることができます
以下の正規表現は。あまりが0から変化しない[0369]
、あまりが1増えてから0に戻る[147](?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147])
、あまりが2増えてから0に戻る[258](?:[0369]|[258][0369]*[147])*(?:[147]|[258][0369]*[258])
のそれぞれの繰り返しを表しています
わかりやすくするために改行を入れると、以下のそれぞれの3つの行の部位が上で説明した3つの遷移に対応しています
(?: [0369] | [147](?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147]) | [258](?:[0369]|[258][0369]*[147])*(?:[147]|[258][0369]*[258]) )+
可視化したらこんな感じになりました
文字列の置換
というわけで3の倍数と5の倍数が求められるようになったので15の倍数も求めることができるようになっています
後は簡単にコマンドで置換している部分を説明しておきます
一つ目の置換までを適用すると、5の倍数の最後にBuzz
が付けられます
s/^(\d*[05])$/$1Buzz/;
1 2 3 4 5Buzz 6 7 8 9 10Buzz 11 12 13 14 15Buzz 16 17 18 19 20Buzz
2つ目の正規表現では、3の倍数の後にFizz
を付けます
s/^((?:[0369]|[147](?:[0369]|[147][0369]*[258])*(?:[258]|[147][0369]*[147])|[258](?:[0369]|[258][0369]*[147])*(?:[147]|[258][0369]*[258]))+)(?=\D)/$1Fizz/;
1 2 3Fizz 4 5Buzz 6Fizz 7 8 9Fizz 10Buzz 11 12Fizz 13 14 15FizzBuzz 16 17 18Fizz 19 20Buzz
最後にFizz
やBuzz
が付いている行の数字を消して終わりです
s/^\d+(?=B|F)//
1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz
追記
以下の記事で少し正規表現を改良しました
sucrose.hatenablog.com
改良後の正規表現
seq 1 10000 | perl -pe 's/^(\d*[05])$/$1Buzz/;s/^(?>([0369]|([147](?1)*)(?:([258](?1)*)|(?2){2})|(?3)(?:(?2)|(?3){2})))+(?=\D)/$1Fizz/;s/^\d+(?=B|F)//