日々之迷歩

世の中わからんことだらけ

ITが複雑で難しくなっていく様に翻弄される日々です。微力ながら共著させていただいた「シェル・ワンライナー160本ノック」をよろしくお願い申し上げます。

【続】拡張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