【続】拡張FizzBuzz問題をシェルプログラミングで
先日拡張FizzBuzz問題をシェルプログラミングで解いた。しかし、シェル芸勉強会の過去問を解いていたら、もっと簡単な方法で解いてあったのだ。
前回の記事はこちら。 papiro.hatenablog.jp
ということで、もっと簡単な方法で拡張FizzBuzzを解いてみることにする。解き方のポイントは下記の通り。
$ seq 1 20 |
awk '$1%3==0{printf "Fizz"}$1%5==0{printf "Buzz"}{print " " $1}'
1
2
Fizz 3
4
Buzz 5
Fizz 6
7
8
Fizz 9
Buzz 10
11
Fizz 12
13
14
FizzBuzz 15
16
17
Fizz 18
19
Buzz 20
FizzやBuzzなどの文字を数字の前に挿入することだけ。後は数字の前に空白を入れて表示するだけ。とにかくやることが簡単というのがポイント。
$ seq 1 20 |
awk '$1%3==0{printf "Fizz"}$1%5==0{printf "Buzz"}{print " " $1}' |
awk '{print $1}'
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
16
17
Fizz
19
Buzz
と言うことで、上記の処理を行うawkスクリプトを生成してやれば良い。
じゃあこのawkスクリプトをawkで作ってみる、ということが考えられるが・・・
$ cat input
3 Fizz
5 Buzz
7 Jazz
$ cat input |
awk '{print "NR%" $1 "==0{printf \"" $2 "\"}"}END{print "{print \" \"$0}"}'
NR%3==0{printf "Fizz"}
NR%5==0{printf "Buzz"}
NR%7==0{printf "Jazz"}
{print " "$0}
・・・という感じで、クォーティングとバックスラッシュによるエスケープがゴチャラゴチャラとした感じでちょっとゲンナリする。そこでここはTukubaiコマンドの力を借りることにする。
Tukubaiコマンドにはテンプレートにデータを埋め込むような処理が出来るmojihame
というコマンドがある。使い方は下記の通り。-lオプションで指定された文字列(例ではLABEL)に挟まれた部分が、データの行数だけ繰り返して処理されるのだ。
$ cat input
3 Fizz
5 Buzz
7 Jazz
$ cat template
LABEL
NR%%1==0{printf "%2"}
LABEL
{print " "$0}
$ mojihame -lLABEL template input
NR%3==0{printf "Fizz"}
NR%5==0{printf "Buzz"}
NR%7==0{printf "Jazz"}
{print " "$0}
templateの中に記載された%1
と%2
の場所に、入力データのデータが左から順番に埋め込まれる。やはりテンプレートがシンプルなテキストデータになっていると、わかりやすくてスッキリする。
テンプレートやデータは標準入力を指定することが出来る。使い方は下記の通り。
$ cat input | mojihame -lLABEL template -
NR%3==0{printf "Fizz"}
NR%5==0{printf "Buzz"}
NR%7==0{printf "Jazz"}
{print " "$0}
以上を踏まえて、拡張FizzBuzz問題をシェルプログラミング解いてみる。
efizzbuzz2.sh
#!/bin/bash tmp=/tmp/$$ num=30 [ $# -eq 1 ] && num=$1 cat <<'EOF' > $tmp-template LABEL NR%%1==0{printf "%2"} LABEL {print " "$0} EOF sed '/^$/d' | mojihame -lLABEL $tmp-template - > $tmp-awkscript seq $num | awk -f $tmp-awkscript | awk '{print $1}' rm -f $tmp*
awkスクリプト生成のためのテンプレートは、一時ファイルとして作成。実際の処理は大幅に減ったのがわかると思う。コードを生成しているということは、ある意味メタプログラミングのような意味合いがあるのではないか?
実行例は下記の通り。
$ cat input
2 Bizz
3 Fizz
5 Buzz
7 Kuzz
$ cat input | ./efizzbuzz2.sh 100
1
Bizz
Fizz
Bizz
Buzz
BizzFizz
Kuzz
Bizz
Fizz
BizzBuzz
....
Kuzz
Bizz
Fizz
Bizz
Buzz
BizzFizz
97
BizzKuzz
Fizz
BizzBuzz