剰余を使わずにFizzBuzz
新幹線の中の暇な時間を使ってやってみた。
まず剰余を使わない方法
最小公倍数と整数論における法の理屈が分かっていれば、3と5の最小公倍数の15周期で"Fizz", "Buzz", "FizzBuzz"のパターンが現れることがすぐに理解できる。ちうことで、"Fizz", "Buzz", "FizzBuzz", NULLからなるの長さ15の配列を用意して、それを順繰りにトレースして行けばいい。
const char* fizz = "Fizz"; const char* buzz = "Buzz"; const char* fibuzz = "FizzBuzz"; const char* fb[] = { NULL, NULL, fizz, NULL, buzz, fizz, NULL, NULL, fizz, buzz, NULL, fizz, NULL, NULL, fibuzz, }; void fizzbuzz() { int c = 1; while (c < 100) { for (int j = 0; (j < 15 && c <= 100); j++, c++) { if (fb[j]) cout << fb[j]; else cout << c; cout << endl; } }; }
ワンライナー化を目指してみる
ついでだから、fizzbuzz関数のワンライナー化も目指してみた。C++は無名関数が使えないので、本当の意味でのワンライナー化はできないけど、それは置いておいて。
if構文を潰す
void fizzbuzz() { int c = 1; while (c < 100) { for (int j = 0; (j < 15 && c <= 100); j++, c++) { ((fb[j])? cout << fb[j]: cout << c) << endl; } }; }
ostream::operator<<() は、自身への参照が返る(だから、cout << foo << bar; ができる)ので、?:の三項演算子を使ってひとつの文にすることができた。
ループを無くす
これはもう、末尾再帰で文句無し。
void fizzbuzz(const unsigned int c, const unsigned int j) { if (c > 100) { return; } ((fb[j])? cout << fb[j]: cout << c) << endl; fizzbuzz(c + 1, (j < 14)? j + 1: 0); }
再帰の基底条件の分岐を潰す
三項演算子を使って、再帰の基底条件に達したときに再帰を終了する分岐のif構文を潰す。そのためにfizzbuzz関数が返り値を持たなければいけないので、とりあえずboolに。
bool fizzbuzz(const unsigned int c, const unsigned int j) { return (c <= 100)? (((fb[j])? cout << fb[j]: cout << c) << endl, fizzbuzz(c + 1, (j < 14)? j + 1: 0)): false; }
すでにこれでワンライナー化はできているけど、falseの存在がなんか格好わるいので、それを消す。そのためには、&&演算子の遅延評価を使用。(c <= 100)が偽なら、評価はそこで終了。
bool fizzbuzz(const unsigned int c, const unsigned int j) { return (c <= 100) && (((fb[j])? cout << fb[j]: cout << c) << endl, fizzbuzz(c + 1, (j < 14)? j + 1: 0)); }
一応、これで完成。
ワンライナーっぽく物理的にも一行で
bool fizzbuzz(const unsigned int c, const unsigned int j) { return (c <= 100) && (((fb[j])? cout << fb[j]: cout << c) << endl, fizzbuzz(c + 1, (j < 14)? j + 1: 0)); }
しゅーりょー。