剰余を使わずに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)); }

しゅーりょー。