0 はじめに
今年の1月からプログラムの勉強を始めたため、いわゆる「FIZZBUZZプログラム」にチャレンジし、そのときのことをブログにした。
今回は前回のブログの続きである。
1 腕試しにチャレンジ
前回のブログで、利用中の言語PERLとC++、勉強中の言語RUBYとPYTHONを用いて「FIZZBUZZプログラム」を書いた。
その際、「ちゃんと動くプログラムをとにかく書く」ことを目標して、特に「縛り」は設けなかった。
そして、その目的は達成した。
ところで、ウィキペディアのFIZZBUZZ問題の項目には次の記載がある。
(以下、https://ja.wikipedia.org/wiki/Fizz_Buzzより引用)
実際に「制限時間2分以内」「剰余(%記号等)を用いない」「1行でできる限り短く(ワンライナー)」等の縛りでゲーム条件を満たすコード記述の腕試しをする者が続出した。
前回のブログでは、この部分に軽く触れただけだった。
しかし、この部分に踏み込まないのはもったいない。
そこで、今回はこの「縛り」プレイにチャレンジする。
2 ワンライナーに挑戦する
まずは「ワンライナー(1行でできるだけ短く)」に挑戦する。
言語は使い慣れたPERLを用いる。
PERLならプログラミング能力のない私でもできそうだからである。
前回は「while」でループを制御したが、今回は「for」でループを制御する。
また、ワンライナーにするためには3項演算子を用いる必要があるだろうから、これを使う。
以上を考えをソースコードに反映させてみる。
こんな感じかな。
(以下、ソースコード)
for ($i=1;$i<101;$i+=1){(!($i%15))?print" FIZZBUZZ!! ,\n":*1?print" FIZZ! ,":*2?print" BUZZ! ,\n":print" $i ,"))}
(ソースコード終了)
文字数がカウントできるサイト( http://www1.odn.ne.jp/megukuma/count.htm )を使ってソースコードの文字数を調べたところ、129文字となった(スペース抜きで120文字)。
また、ちゃんと動くかチェックする。
ちゃんと動いたようだ。
めでたしめでたし。
3 文字の最小化にチャレンジする
ワンライナーが思ったよりも簡単にできたので、「文字数を減らす方法」を少しだけ考える。
まず、過剰なカッコを外す。
これで何文字か減らせた。
また、改行位置を変える。
前回は5の倍数毎に改行していたが、15の倍数毎の改行に変える。
さらに、構造を変える。
前回のプログラムのforループの中身を日本語にすると次のようになる。
(15の倍数だったら)?(「FIZZBUZZ」と書け):
((3の倍数だったら)?(「FIZZ」と書け):
((5の倍数だったら)?(「BUZZ」と書け):(数字を書け)))
この構造だと「と書け」の部分が何度も現れている。
よって、この重複を最初に括れば文字数が減らせる。
以上3点を修正したソースコードは次のとおりである。
(以下、ソースコード)
for($i=1;$i<101;$i+=1){print !($i%15)?" FIZZBUZZ!!,\n":(!($i%3)?" FIZZ!,":(!($i%5)?" BUZZ!,":" $i,"))}
(ソースコード終了)
実行結果はこんな感じ。
ちゃんと動いた。
また、文字数は129(スペース抜きで120)から102(97)となった。
これなら成功(目的達成)と言っていいだろう。
以上、ワンライナーに挑戦した。
予想よりも簡単にできた。
また、3項演算子を教科書以外の場所で初めて使ったが、予想より簡単に使えた。
今後も3項演算子を使いこなしていきたい。
3 「剰余を用いない」にチャレンジする
次に、「剰余(%記号等)を用いない」にチャレンジしてみる。
最初に思いついた方法は再帰関数を用いた次の方法である。
(以下、ソースコード)
for ( $i = 1 ; $i <= 100 ; $i += 1 ) { print &FBoutput($i) ; }
sub FBoutput {
if ( $_[0] > 15 ) { return &FBoutput($_[0]-15) ; }
elsif ( $_[0] == 15 ) { return " FIZZBUZZ!!,\n" ; }
elsif ( $_[0] == 3 or $_[0] == 6 or $_[0] == 9 or $_[0] == 12) { return " FIZZ!," ; }
elsif ( $_[0] == 5 or $_[0] == 10 ) { return " BUZZ!," ; }
else { return " $i," ; } }
(ソースコード終了)
関数(サブルーチン)を作り、ループの中をすっきりさせた
(ワンライナーにこだわる必要がないので、多少長くなっても問題ない)。
この関数の部分を日本語に変換すると次のとおりになる。
①16以上だったら15を引き、関数の最初の部分に戻す
②15以下で15だったら「FIZZBUZZ!!」を返す
③3、6、9、12だったら「FIZZ!」を返す
④5、10だったら「BUZZ!」を返す
⑤それ以なら数値を返す
このプログラムでは剰余を用いていない。
また、この関数はどんな数字を入力しても対応できる。
そして、ちゃんと動く(実行画面は省略)。
これなら、一応「ミッションクリア」と言えるだろう。
この点、この関数には複数のreturnがある。
だから、3項演算子を使って文字数・行数を減らすことができる。
しかし、今回は文字数の縛りがない。
また、3項演算子を使うとかえって分かりにくくなりそうである。
よって、3項演算子は使わないし、その形に修正することもしない。
また、今回は再帰関数を用いた。
しかし、次のソースコードのようにwhileを使う方法もありそうである。
(以下、ソースコード)
for ( $i = 1 ; $i <= 100 ; $i += 1 ) { print &FBoutput($i) ; }
sub FBoutput {
$j = $_[0] ;
while ( true ) {
if ( $j > 15 ) { $j -= 15 ; }
elsif ( $j == 15 ) { return " FIZZBUZZ!!,\n" ; }
elsif ( $j == 3 or $j == 6 or $j == 9 or $j == 12) { return " FIZZ!," ; }
elsif ( $j == 5 or $j == 10 ) { return " BUZZ!," ; }
else { return " $i," ; } } }
(ソースコード終了)
どっちがいいかはよくわからない。
まあ、両方とも書けて、かつ、動いたので、「両方正解」でいいかな。
以上、「剰余を用いない」という条件もクリアできた。
他にも面白い縛りがあればやってみたい。
また、今回はPERLを用いたが、「練習」という点を考慮すれば、RUBYやPYTHONでもチャレンジすべきかもしれない。
特に、「①剰余は使わない。②if等を用いず3項演算子を用いる。③再帰関数を必ず用いる。」という条件で書くというのは練習になりそうである。
このブログを公開した後にでも挑戦しよう。
そもそもRUBYやPYTHONについては「特に何かを参照することなくプログラムを作成できるようになる」という目標もあることだし。