今更だけどFizzBuzz問題(正規表現で)
2010-01-06-1 / カテゴリ: [programming][正規表現][perl] / [permlink]

更にPerlネタ

昨日([2010-01-05-1])に引き続き他人の日記をこっそり覗き見しているとFizzBuzz問題のPerlのコードを発見
2年位前に結構盛り上がってたみたいだけど、何故かリアルタイム参戦してなかったので、ちょっとやってみた。

が、何のヒネリも入らなかった。
perl -le 'print (($_%15==0)?"FizzBuzz":($_%3==0)?"Fizz":($_%5==0)?"Buzz":$_) for (1..100)'

んで、件のエントリに
剰余を使うな

とかありました
とあったし、ワンライナーは出尽くしてるっぽいので、剰余禁止(ついでに乗除算禁止、変換テーブル禁止)で、Perl使いらしく正規表現(キャプチャ禁止)でマニアックに実装してみた。
(ググったらそのものズバリな正規表現でFizzBuzzは見つかったけど、手持ちスキルはPerl5.8止まりなのでよくわからんかったorz <- 何がPerl使いだ>自分)

#!/usr/bin/perl -l
for (1..100) {
  s/(?<=[50])$/Buzz/;
  for (($a = $_) =~ s/\D*//g; $a !~ /^.$/; $a =~ s/(\d)(\d)/$1+$2/eg){}
  s/(?=$|\D)/Fizz/ if $a =~ /[369]/;
  s/\d+(?=\D)//;
  print;
}

とりあえず解説。
処理の流れ: 5倍数時は"数字Buzz"に置換、3倍数時は"数字Fizz"に置換、最後に数字以外が付加されていれば数字を削除して出力する。先に5を処理してるのは "5か0 + (非数字があるか空)" のパターンでキャプチャ不使用で末尾にBuzzを付加するパターン置換がどうしてもできなかったからorz

  • 1行目: シェバングは -l 付き。print; 単体で手抜き出力させるため:p
  • 2-8行目: 1から100までループ
  • 3行目: 「5の倍数は1桁目が0か5」なので、末尾($)が5or0([50])の場合に、数字部分を残して((?<=))末尾のみをBuzzに置換。数字は3の倍数判定に使用するので消さない。
  • 4行目: 「3の倍数は各桁の数字の和が3で割り切れる」ので、各桁の和を求める処理を1桁になるまで繰り返して3or6or9なら3の倍数ってことで、4行目のfor()の初期処理で倍数判定処理用に一時変数に一旦コピー(これがかなり気に入らん)、ただし既に5の倍数時はBuzzが付加されているので、数字以外(\D*)はコピー後($a = $_)に消去(s///g)、継続条件式で1桁判定(length($a)!=1に置換え可)、更新式で"数数"を((\d)(\d))数+数($1+$2/e)に全部(g)置換(s///)する。
  • 5行目: 「元のループ変数($_)」を1桁になるまで求めた各桁の和($a)が3or6or9(/[369]/)だった場合のみ(if修飾子)置換。ただし5の処理で既にBuzzが付いてる場合があるので、次の文字((?=))が末尾か非数字($|\D)の位置を置換。
  • 6行目: この時点で、数字のみ or 数字+Fizz or 数字+Buzz or 数字+FizzBuzz になってるので、次の文字((?=))が非数字(\D)の数字全て(\d+)を消去(s///);
  • 7行目: 出力
てか、Perlって空処理の制御構文でもブレース省略できないのね。
Cだと
for (...);
ってできるけど。


あ、多分当時はCに毒されてPerlを忘れつつあったから、面倒がって流行に乗らなかったのかも。(2007年のprogrammingカテがCばっか…)

置換時に式を評価する
2007-02-09-3 / カテゴリ: [perl][正規表現] / [permlink]

/e で。割とよく使われてるはずだからサンプルだけ
C/C++ ソースの #ifdef を削る。
@define = qw/__DEBUG __WRITELOG/; # define を指定する

# $src にソースを丸ごとつっこむ

# #if-#endif
$src =~ s|^#if\s+(\w+)(.*?)^(#else(.*?))?^#endif|($1)?$2:$4|emsg;
# #ifdef-#endif
$src =~ s|^#ifdef\s+(\w+)(.*?)^(#else(.*?))?^#endif|(grep {$_ eq $1 } @define)?$2:$4|emsg;
# #ifndef-#endif
$src =~ s|^#ifndef\s+(\w+)(.*?)^(#else(.*?))?^#endif|(grep {$_ eq $1 } @define)?$4:$2|emsg;
/m で複数行マッチ、/s で . を改行にマッチさせる。
/m で複数行マッチ処理を行ってる場合、式の途中の ^ は改行直後の位置にマッチする。
else 節がない場合は $4 は空になるのでバッサリ削られる。

前回のマッチ位置から処理する
2007-02-09-2 / カテゴリ: [perl][正規表現] / [permlink]

単に繰り返したい場合は
/regexp/g;
/g オプションで、繰り返し処理される。
けど、ある正規表現にマッチした場合、その内容によって以降の処理が変化させたい、などのちょっと複雑(?)な場合は、/c /g によって、マッチ箇所を記憶させ、次に \G でその位置を参照できる。
続きを読む

エスケープされていないダブルクォート
2007-02-09-1 / カテゴリ: [perl][正規表現] / [permlink]

こんな感じかな。
$reg = qr/(?<!\\)(?:\\\\)*\"/;
手前に \ がなくて、\\ が0個以上あって " の場合。
否定後読みの (?<!\\) がないと、奇数個の逆スラッシュは、偶数個分だけが(?:\\\\)*にマッチしてしまうので NG。

/以外のデリミタで置換
2005-07-11-1 / カテゴリ: [unix][linux][正規表現][vi] / [permlink]

:%s#/export/home/#/home/#g
フツーにできますな。

Solaris8 の /bin/vi でもできた。

ascii文字の正規表現
2005-07-08-2 / カテゴリ: [programming][perl][command][正規表現] / [permlink]

m/[ -~]/
0x20(スペース)から0x7e(チルダ)まで。
0x20未満のハードタブ(0x09)や改行(0x0A)は個別に対処せよ。
0x7F(DEL)はいらねーよな。

あぁ、grep でも使える
$ command | grep -v '[ -~]'
asciiを含まない行を出力
$ command | grep '[^ -~]'
ascii以外を含む行を出力

lv(v.4.50, v.4.51)の正規表現検索は、スペースを範囲に含めると overcrossing range と出力されて効かないので、0x21の!から指定する
/[^ !-~]
ascii以外(タブなど除く)を含む行を出力

less(351, 358)は [ -~]で大丈夫なんだけどなぁ。意外にも more も大丈夫だ。

sed のマッチ箇所のキャプチャ
2005-03-09-1 / カテゴリ: [unix][linux][正規表現][command] / [permlink]

% sed -e 's/\(regex\)/\1/'
"逆スラ番号"で
カテゴリ: 正規表現

最終更新時間: 2013-05-02 16:12