やっさんの雑記

プログラミングでやってみたこととか

C言語のプリプロセッサでFizzBuzz

alucky0707さんの下の2記事に触発されて(かつ参考にしながら)C言語プリプロセッサだけでFizzBuzzを書いてみました。
インクルードファイルの階層の深さの制限とかは(事実上)なくなるようにしました。

CPP(コンパイルしない方の関数型なC言語)プログラミング入門。とりあえずFizzBuzzまで - Qiita
http://qiita.com/alucky0707/items/3599cdcf973382df978b
C言語 - MSVCでもCPPでFizzBuzzしてみた - Qiita
http://qiita.com/alucky0707/items/d4073a9a3af9a804477a

下のソースコードを普通にコンパイルしてできる実行可能ファイルを実行すると、1000までのFizzBuzzができます。
FizzBuzzの上限数を変えたい場合は、(例えば31415までやりたいとかだとすると)gccだと

$ gcc -D"FIZZBUZZ_MAX=31415" fizzbuzz.c

MSVCだと

> cl /DFIZZBUZZ_MAX=31415 fizzbuzz.c

とかでコンパイルすればいいと思います(ソースコード上は5桁までしか対応してないですが、適宜拡張すれば桁数はいくらでも増やせます)。

それではとりあえずソースコード全体です。

# ifndef __FIZZBUZZ__
#
#   define __FIZZBUZZ__
#
#   ifndef FIZZBUZZ_MAX
#     define FIZZBUZZ_MAX 1000
#   endif
#
#   define FIZZ_FLAG ((COUNTER % 3 == 0) && (0 < COUNTER))
#   define BUZZ_FLAG ((COUNTER % 5 == 0) && (0 < COUNTER))
#
#   define c0 0
#   define c1 0
#   define c2 0
#   define c3 0
#   define c4 0
#
#   define TOSTRING_(x) #x
#   define TOSTRING(x) TOSTRING_(x)
#   define JOIN_(a, b) a##b
#   define JOIN(a, b) JOIN_(a, b)
#
#   define ctr4 c4
#   define ctr3 JOIN(c3, ctr4)
#   define ctr2 JOIN(c2, ctr3)
#   define ctr1 JOIN(c1, ctr2)
#   define ctr0 JOIN(c0, ctr1)
#   define COUNTER
#
#   define DEPTH 0
#
# endif


# if   c0 != 0
#   undef  COUNTER
#   define COUNTER ctr0
# elif c1 != 0
#   undef  COUNTER
#   define COUNTER ctr1
# elif c2 != 0
#   undef  COUNTER
#   define COUNTER ctr2
# elif c3 != 0
#   undef  COUNTER
#   define COUNTER ctr3
# else
#   undef  COUNTER
#   define COUNTER ctr4
# endif


# if DEPTH == 0
#
    #include <stdio.h>
    int main()
    {
#
#   undef  DEPTH
#   define DEPTH 1
#
#   undef  c0
#   define c0 0
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 1
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 2
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 3
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 4
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 5
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 6
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 7
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 8
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 9
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c0
#   define c0 0
#
#   undef  DEPTH
#   define DEPTH 0
#
        return 0;
    }
#
# elif DEPTH == 1
#
#   undef  DEPTH
#   define DEPTH 2
#
#   undef  c1
#   define c1 0
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 1
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 2
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 3
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 4
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 5
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 6
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 7
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 8
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 9
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c1
#   define c1 0
#
#   undef  DEPTH
#   define DEPTH 1
#
# elif DEPTH == 2
#
#   undef  DEPTH
#   define DEPTH 3
#
#   undef  c2
#   define c2 0
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 1
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 2
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 3
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 4
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 5
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 6
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 7
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 8
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 9
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c2
#   define c2 0
#
#   undef  DEPTH
#   define DEPTH 2
#
# elif DEPTH == 3
#
#   undef  DEPTH
#   define DEPTH 4
#
#   undef  c3
#   define c3 0
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 1
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 2
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 3
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 4
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 5
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 6
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 7
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 8
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 9
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c3
#   define c3 0
#
#   undef  DEPTH
#   define DEPTH 3
#
# elif DEPTH == 4
#
#   undef  DEPTH
#   define DEPTH 5
#
#   undef  c4
#   define c4 0
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 1
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 2
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 3
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 4
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 5
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 6
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 7
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 8
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 9
#     if COUNTER <= FIZZBUZZ_MAX
#       include __FILE__
#     endif
#   undef  c4
#   define c4 0
#
#   undef  DEPTH
#   define DEPTH 4
#
# elif DEPTH == 5
#
#   if FIZZ_FLAG && BUZZ_FLAG
      printf("FizzBuzz\n");
#   elif FIZZ_FLAG
      printf("Fizz\n");
#   elif BUZZ_FLAG
      printf("Buzz\n");
#   elif 0 < COUNTER
      printf(TOSTRING(COUNTER)"\n");
#   endif
#
# endif

以下適当に解説します。

カウンターは十進数の各桁に分割して管理します。
元のコードでは単にループして頑張って各桁の数字を書き換えるものでしたが、インクルードファイルの階層ごとに扱う桁を分けて深さ優先探索的なことをすることで事実上の制限をなくしました。
イメージ的にはこんなCのコードを実行している感じ。
fizzbuzz1()がalucky0707さんのFizzBuzzのイメージで、fizzbuzz2()が私のFizzBuzzのイメージです。

int c0 = 0;
int c1 = 0;
int c2 = 0;
int c3 = 0;
int c4 = 0;

void counter(); // returns the value of the counter

// alucky0707's fizzbuzz
void fizzbuzz1()
{
    if (c4 == 0) {
        c4 = 1;
    } else if (c4 == 1) {
        // 
        // ...
        //
    } else if (c4 == 9) {
        c4 = 0;
        if (c3 == 0) {
            // 
            // ...
            //
        }
    }
    //
    // write fizz buzz...
    //
    fizzbuzz1();
}

// my fizzbuzz
void fizzbuzz2(int depth)
{
    if (depth == 0) {
        ++depth;
        c0 = 0;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
        c0 = 1;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
        // 
        // ...
        // 
        c0 = 9;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
    } else if (depth == 1) {
        ++depth;
        c1 = 0;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
        c1 = 1;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
        // 
        // ...
        // 
        c1 = 9;
        if (counter() <= FIZZBUZZ_MAX) fizzbuzz2(depth);
    } else if (depth == 2) {
        // 
        // ...
        // 
    } else if (depth == 5) {
        // 
        // write fizz buzz...
        // 
    }
}

ちなみにこの方法だと、FizzBuzzできる最大数を制限するのはインクルードファイルの階層数よりももはや数値定数の最大値だとかコンパイル時間だとかそっちになってくると思います。

もっとコードを短くスマートにできるのかもしれませんが、私にはそのスキルはありませんでした。

コンパイル時実行バンザイ!

04/29 20:54追記

gccだと私の環境ではFIZZBUZZ_MAX=27622くらいでgccのメモリ不足(?)でコンパイルできなくなりました。
MSVCはFIZZBUZZ_MAX=99999でも(コンパイルに時間はかかりますが)コンパイルできます。