色々なFizzBuzz【3行 1行 空白無 再帰 軽量 変態 コンパイル時】

3行FizzBuzz

ロジック部だけで3行のFizz Buzzです。少し反則的なテクニックを使ってますが、比較的読みやすい部類に入ると思います。ジャンル的には「if文を使わないFizzBuzz」となります。

#include <stdio.h>

int main() {
  char *ary[] = {"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"};
  for (int i = 1; i <= 100; i++)
    printf(ary[(i % 3 ? 0 : 1) + (i % 5 ? 0 : 2)], i);
}
スポンサーリンク

解説

FizzBuzz判定部

ループ内の(i % 3 ? 0 : 1) + (i % 5 ? 0 : 2)という式で数値iをグループ化しています(0:数字 1:Fizz 2:Buzz 3:FizzBuzz)。グループ化された値は配列のインデックスに対応しており、まさに配列{"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"}の添字として使われています。

よってary[(i % 3 ? 0 : 1) + (i % 5 ? 0 : 2)]の段階でFizzBuzzの判定は完了です。あとは表示するだけです。

表示部

取得した配列要素をprintf関数のフォーマット文字列として使用しています。文字列が%d\nの場合は第二引数に指定された現在の数値がそのまま表示されます。

文字列が%d\n以外のFizz\nBuzz\n, FizzBuzz\nだった場合は第二引数は無視され、文字列がそのまま出力されます。

けいおん部

「けいおん! college」はけいおんの続編です。大学生編を描いたcollegeの他に、高校に取り残されたあずにゃんの活躍を描いたhighschoolというスピンオフ的な続編もあります。

2行FizzBuzz

ロジック部分を2行に短縮することも可能です。こちらは前回と同等のロジックで、if文や条件演算子を一切行わないジャンルのFizzBuzzです。

for (int i = 1; i <= 100; i++)
  printf((char*[]){"%d\n", "Fizz\n", "Buzz\n", "FizzBuzz\n"}[!(i % 3) + !(i % 5) * 2], i);

1行FizzBuzz

ちなみにRubyなら一行で書けます。

#!/usr/bin/ruby
(1..100).each{|i|printf(["%d","Fizz","Buzz","FizzBuzz"][(i%3==0?1:0)+(i%5==0?2:0)]+"\n",i);}

少しひねった短縮版です。

#!/usr/bin/ruby
1.upto(100){|i|puts [i,"Fizz","Buzz","FizzBuzz"][(i%3<1?1:0)+(i%5<1?2:0)]}

変態FizzBuzz

こちらは変態FizzBuzzです。様々なテクニックを詰め込んでいます。教育用にどうぞ。 ジャンル的にはFizzとBuzzを複数回使用しないスタイルのFizzBuzzになります。

#include <stdio.h>
#include <string.h>

int main() {
  for (int i = 1; i < 101; i++) {
    char str[] = "\n\0\n\0\n\0\n\0\n\0\n\0\n\0\n\0%dFizzBuzz";
    int s = !(i % 3) + !(i % 5) * 2;
    char* ofs = str + 16 + (s ? s * 4 - 2 & 0b0111 : 0);
    strncpy(str, ofs, ((s & 1) + (s >> 1)) << 2 ?: 2);
    printf(str, i);
  }
}

ちなみにロジック部は一行に短縮できます。

for (int i = 1, s; s = !(i % 3) + !(i % 5) * 2, i < 101; i++) printf(strncpy((char[]){"\n\0\n\0\n\0\n\0\n\0\n\0\n\0\n"}, "%dFizzBuzz" + (s ? s * 4 - 2 & 0b111 : 0), ((s & 1) + (s >> 1)) << 2 ?: 2), i);

空白無しFizzBuzz

警告を無視すれば、完全に一行で書けます。こちらは空白を用いないジャンルのFizzBuzzです。

s;main(i){for(i=1;s=!(i%3)+!(i%5)*2,i<101;i++)printf(strncpy((char[]){"\n\0\n\0\n\0\n\0\n\0\n\0\n\0\n"},"%dFizzBuzz"+(s?s*4-2&7:0),((s&1)+(s>>1))<<2?:2),i);}

ワンライナーFizzBuzz

コマンドライン上でワンライナーもできます。

echo "i;main(s){for(i=1;s=(i%3<1)+(i%5<1)*2,i<101;i++)printf(strncpy((char[]){\"\n\0\n\0\n\0\n\0\n\0\n\0\n\0\n\"},\"%dFizzBuzz\"+(s?s*4-2&7:0),((s&1)+(s>>1))<<2?:2),i);}" | clang -w -xc -; ./a.out

軽量FizzBuzz

第二引数の評価順序が少し心配なFizzBuzzです。

s;main(i){for(i=1;s=!(i%3)+!(i%5)*2,i<101;)printf(s?2&s?(4&~2+s)+"FizzBuzz\n":"Fizz\n":"%d\n",i++);}

もっと軽量FizzBuzz

main関数の第一引数int argcが1であることを前提とするFizzBuzzです。

main(i){while(i<101)printf(i%5?i%3?"%d\n":"Fizz\n":"FizzBuzz\n"+4*!!(i%3),i++);}

ちょっと軽量FizzBuzz

イマイチ釈然としないFizzBuzzです。上手くやればもっと軽量化できそうな気がします。

main(i){while(i<101)printf("%d\n\0Fizz\n\0FizzBuzz\n"+!(i%3)*4+(i%5?0:14-8*!(i%3)),i++);}

再帰FizzBuzz

main関数が再帰的に呼び出されて夜も眠れないFizzBuzzです。

int main(int i) {
  printf(i % 5 ? i % 3 ? "%d" : "Fizz" : (4 * !!(i % 3)) + "FizzBuzz", i)
    && puts("")
    && i < 100
    && main(i + 1);
}

ビット演算FizzBuzz

ビット演算とポインタ演算を用いたえげつないFizzBuzzです。

複合リテラル(C99)と右辺値参照(C++)の仕様を活用をしていますが、現状ではClang++等のコンパイラでなければコンパイルできないはずです。

#include <cstdio>
// $ clang++ fizzbuzz.cpp
int main() {
  for(int i = 1, s; s = !(i % 3) + !(i % 5) * 2, i < 101; i++)
    printf(&(((char[]){"%dFizzBuzz"})[(s >> (s & 2) / 2 << ((s & 2) / 2 + 2)) + 2] = '\0') - (2 << ((s % 2) + (s & 2) / 2)), i), printf("\n");
}

SQL FizzBuzz

SQLデータベースでFizzBuzzします。

SQLのダミーテーブルでFizzBuzz

コンパイル時FizzBuzz

コンパイル時にFizzBuzzします。もはや何を言っているのか分からないでしょう。 大丈夫です、私もよくわかってませんので。

コンパイル後、以下の様な実行コードが生成されるイメージです。

int _main() {
  puts("1\n2\nFizz\n4...(以下省略)");
}

かなり高速です。ちなみにC++で書かれています。

#include <cstdio>

constexpr int len(int v, int _n = 1) { return (v = v / 10) ? len(v, _n + 1) : _n; }
constexpr int pow(int e) { return e ? e == 1 ? 10 : pow(e - 1) * 10 : 1; }
constexpr int chr(int v, int e) { return '0' + v / pow(e) % 10; }

constexpr int group(int v) { if (int a = !(v % 3) + !(v % 5) * 2) return -((a * 4 ^ 12 ?: 4) ^ 12); return len(v); }
constexpr int value(int v, int p) { return 0 < p ? chr(v, len(v) - p) : *((char*)"FizzBuzz" + -(++p)); }
constexpr int shift(int v, int p) { return -5 == p && v % 3 ? 0 : p + (0 < p ? -1 : 1); }

template<int P, int V, int... A> struct FizzedBuzzed { static constexpr auto ary = FizzedBuzzed<shift(V + 1, P), V, value(V + 1, P), A...>::ary; };
template<int V, int... A> struct FizzedBuzzed<0, V, A...> { static constexpr auto ary = FizzedBuzzed<group(V), V - 1, '\n', A...>::ary; };
template<int... A> struct FizzedBuzzed<0, 0, A...> { static constexpr char ary[] = {A..., '\0'}; };
template<int... A> constexpr char FizzedBuzzed<0, 0, A...>::ary[];

// clang++ -std=c++14 fizzbuzz.cpp
int main() { puts(FizzedBuzzed<0, 50>::ary); }

さらなる高速化が可能

実はさらに高速化できます。詳しくは別記事のコンパイル時FizzBuzz(地球にやさしい)で確認してみてください。また現状のコードではコンパイラ側の制限で50までの数字にしか対応できないのですが、リンク先の方法であれば100以上数字でFizzBuzzできます。

広告