2008-10-30

http://anond.hatelabo.jp/20081025202001

いまさらだがFizzBuzz

1から100まで、3の倍数5の倍数云々って、全部定数の計算じゃね?

というところに気付き、自称メタプログラマー(略してメタグラマー)俺の血が騒いだ。

定数計算なら、それは実行時ではなくコンパイル時に行なわれるべきだ……。

というわけでC++テンプレートメタプログラミング召喚。

#include <iostream>

const int FIZZ_NUM = 3;
const int BUZZ_NUM = 5;
const int BEGIN_NUM = 1;
const int END_NUM = 101;

template<int N> struct Fizz {
    enum {PRINT = 0, NEXT = N + 1};
    static void print() {}
};

template<int N> struct Buzz {
    enum {PRINT = 0, NEXT = N + 1};
    static void print() {}
};

template<int N, bool ForB> struct Number {static void print() {std::cout << N;}};

template<> struct Fizz<FIZZ_NUM> {
    enum {PRINT = 1, NEXT = 1};
    static void print() {std::cout << "Fizz";}
};

template<> struct Buzz<BUZZ_NUM> {
    enum {PRINT = 1, NEXT = 1};
    static void print() {std::cout << "Buzz";}
};

template<int N> struct Number<N, true> {static void print() {}};

template<int N, int F, int B> struct FizzBuzz {
    static void print() {
        typedef ::Fizz<F> Fizz;
        typedef ::Buzz<B> Buzz;
        
        Fizz::print();
        Buzz::print();
        Number<N, Fizz::PRINT || Buzz::PRINT>::print();
        std::cout << std::endl;
        
        FizzBuzz<N + 1, Fizz::NEXT, Buzz::NEXT>::print();
    }
};

template<int F, int B> struct FizzBuzz<END_NUM, F, B> {static void print() {}};

int main(int argc, char **argv)
{
    FizzBuzz<BEGIN_NUM, 1, 1>::print();
    return 0;
}

ifなし%なしループ系なし、しかも実行時オーバーヘッドなし!(多分)

あ、これを見て理解する人間オーバーヘッドは無視ね。

ああ、久しぶりにC++を触ったけど、やっぱC++テンプレートってダメダメだな。20世紀の遺物といわざるを得ない。

君がもし21世紀モテイケメンメタグラマーなら、21世紀プログラミング言語D言語を使うべきだ!

驚くべきことに、D言語コンパイル時に関数が実行でき、その結果をソースコードとして取り込める!

ただし実行できるのは簡単な関数だけだけど……。

以下、それを使ったD言語によるメタプログラミング的実装。

import std.stdio;

// これでFizzBuzzを全部出力するコードを作るぜ!
string makeFizzBuzzCode() {
    string code;
    for(int i = 1; i <= 100; ++i) {
        // 効率? コンパイル時にそんな配慮は要らん!
        if(i % 3 == 0 &amp;&amp; i % 5 == 0) {
            code ~= "writefln(\"FizzBuzz\");\n";
        } else if(i % 3 == 0) {
            code ~= "writefln(\"Fizz\");\n";
        } else if(i % 5 == 0) {
            code ~= "writefln(\"Buzz\");\n";
        } else {
            code ~= "writefln(" ~ static_itoa(i) ~ ");\n";
        }
    }
    return code;
}

int main(string[] args) {
    // おまけで生成されたコードも見せるよ。
    pragma(msg, makeFizzBuzzCode());
    
    // 生成したコードを埋め込む。コピペみたいな感覚
    mixin(makeFizzBuzzCode);
    return 0;
}

// 以下ユーティリティ。このぐらい標準で欲しいな……。

/// 整数文字列変換(コンパイル時)
string static_itoa(int n) {
    if(n == 0) {
        return "0";
    }
    
    // 10で割りながら余りを文字にして追加。桁が逆転した文字列になる。
    string s;
    for(; n; n /= 10) {
        s ~= ("0123456789")[n % 10];
    }
    
    // 桁位置を正常にする。相変わらず効率無視。
    return static_reverse(s);
}

/// 配列リバースコンパイル時)
/// 実行時ならarray.reverseが使えるんだけどね……。
T[] static_reverse(T)(T[] s) {
    T[] result;
    foreach_reverse(c; s) {
        result ~= c;
    }
    return result;
}

// 心配なので静的ユニットテスト(笑)
unittest {
    static assert(static_itoa(0) == "0");
    static assert(static_itoa(10) == "10");
    static assert(static_itoa(999) == "999");
    static assert(static_itoa(9999) == "9999");
    static assert(static_itoa(12345) == "12345");
    static assert(static_itoa(314159265) == "314159265");
}

コンパイル結果

$ dmd -unittest fizz_buzz.d
writefln(1);
writefln(2);
writefln("Fizz");
writefln(4);
writefln("Buzz");
writefln("Fizz");
writefln(7);
writefln(8);
writefln("Fizz");
writefln("Buzz");
writefln(11);
writefln("Fizz");
writefln(13);
writefln(14);
writefln("FizzBu(ry

出力結果は略。

さすがD言語C++JavaC#にできない事を平然とやってのけるッ

そこにシビれる!あこがれるゥ!

というか、

writefln(1);
writefln(2);
writefln("Fizz");
writefln(4);

もうwritefln(出力関数)要らなくね?

修正。

// これでFizzBuzzを全部出力するぜ!
string makeFizzBuzzCode() {
    string code;
    for(int i = 1; i <= 100; ++i) {
        // 効率? コンパイル時にそんな配慮は要らん!
        if(i % 3 == 0 &amp;&amp; i % 5 == 0) {
            code ~= "FizzBuzz\n";
        } else if(i % 3 == 0) {
            code ~= "Fizz\n";
        } else if(i % 5 == 0) {
            code ~= "Buzz\n";
        } else {
            code ~= static_itoa(i) ~ "\n";
        }
    }
    return code;
}

int main(string[] args) {
    // もうコンパイル時のメッセージしか出さない。(笑)
    pragma(msg, makeFizzBuzzCode());
    return 0;
}

コンパイル結果。

$ dmd -unittest fizz_buzz.d
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBu(ry

実行するまでもなく結果が出力された。つまり実行時間ゼロ、ということは……

世 界 最 速

以上、世界最速なD言語宣伝でした。

みんな使おうD言語

D言語リファレンス日本語

http://www.kmonos.net/alang/d/1.0/index.html(1.0。こっちの方が安定してる?)

http://www.kmonos.net/alang/d/2.0/index.html(もっと凄い2.0)

D言語本家配布元(無料コンパイラが入手できます)

http://www.digitalmars.com/

それにしても、ちゃんとD言語シンタックスハイライトを実装しているはてなは偉い。(笑)

記事への反応 -
  • 私は今某社でWebアプリ開発の補助をするアルバイトをしている。一応少しはコードを書く。でも、自分はプログラマじゃないな、と思う。素敵なアルゴリズムを考えたりもしない。アプリ...

    • いまさらだがFizzBuzz。 1から100まで、3の倍数5の倍数云々って、全部定数の計算じゃね? というところに気付き、自称メタプログラマー(略してメタグラマー)俺の血が騒いだ。 定数計...

      • ん?ん? なんだ・・・? そんなの計算するまでもなく writeln("Fizz"); writeln("Buzz"); とか記述していけばいいだけの話で、改行コードもあわせて出力しちゃえば1行でいけるぜ! みたいな...

        • まあネタなんだけど(笑) マジな要素を抽出するとしたら、 1から100までコードをべた書きするのが実行速度的には効率的なんだけど、 それを手で書くのはバカらしいので、 コンパイル...

    • main(void)とか、intの関数のくせにreturn値なしとか、ふざけてるの?死ぬの? とか思ったがそれは置いといて。 アホか。 勉強しなきゃならないのはどんな職業でも一緒だよ。そんなのバイ...

      • C99及びC++では値を返さなければ0を返したことになるんだな。 死んじゃうのはあなたのほうかもしれないよ。

        • 嘘言うな。 X3010 105頁 6.8.6.4、79頁6.6.3 2節には 「戻り値の型がvoid(値を返さない)と定義されている場合を除いて、式の省略はできない」 と書かれてるぞ。 おい、お前の言ってる0を返すっ...

          • まず、1999年版を見てるよな?C90じゃなく。 で本題だが、main関数についての特例があるからもっとよく読んでご覧。

            • 補足。C++(14882:2003)だと、3.6.1の5に書いてある。 Cはいま手元にないので自分で探してね。

            • ソース出せという問いに、その反応で満足するやつはプログラマじゃないな。 で、俺は思うんだが元増田がC99あるいはC++であるという根拠は全く無いし、業界標準であるgccだってC99準拠...

              • やれやれ、面倒くさい子だね。Cのpdfを眺めてみたよ。9899:1999の5.1.2.2.3に載ってるから確認してくださいな。 GCCについてはここを見てね。http://gcc.gnu.org/c99status.html 鼻息荒いからからかっ...

                • 別増田なんだけど、戻り値無くてもいいとは言われても、世に出てるコンパイラの殆どが警告出すんじゃないの? gccもVC++も出すよね。 警告無視派?「linuxカーネルなんて何百行警告出る...

                  • 警告無視派じゃないよ。むしろ警告をエラーとして扱う派。 int main() {} については、g++だと-Wall -Wでも警告なし、gccだと警告あり、gcc -std=c99だと-Wall -Wでも警告なし、ですね。

                    • ええええええーー じゃあ、gccで警告出るんだから、エラー扱いしろよ。 何擁護してるの?バカなの、やっぱり死ぬの?

                      • だからね、あなたにとっては古いCが世界の全てかもしれないけれど、世の中全てのひとがそうじゃないんだよ、前提はいろいろなんだよ、と言ってるわけ。 多様な前提を許容していない...

                      • だからね、あなたにとっては古いCが世界の全てかもしれないけれど、世の中全てのひとがそうじゃないんだよ、前提はいろいろなんだよ、と言ってるわけ。 多様な前提を許容していない...

    • 向いてないことに早めに気付けて、よかったんじゃないの 自分の適性にあわないことを続けてもねぇ。。。

    • あの…、なんかmain()の仕様で熱くなってる人が居るところ申し訳ないんですけど このFizzBuzz、仕様満たしてないようなきがするんですけど、そこは突っ込まないんですか。

      • すばらしい指摘すぎて全俺が泣いた。 Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of bot...

    • これはwwwww。 mainで議論してるやつ恥ずかしいwwww なんで気づかないのm9(^Д^)

    • そして元増田はDNBK

    • http://anond.hatelabo.jp/20081025202001 トラックバック熱いなーw

    • まあトラバが示しているようにどうでもいい事で悩みこむ性格じゃないとダメだから、むしろなれないほうがいいかもね。 あと、別にコーダでも会社や社会の役に立つし、優秀なプログ...

      • ただ、コーダとなると、韓国人やらインド人やらが幅を利かせてるから、日本人じゃそもそも仕事が取れないわけで。 最低でもプログラマにならないと、そもそも仕事ないんだよ。 特に...

    • http://anond.hatelabo.jp/20081025202001 のコード見ると 3と5の除余判定を2度やってるでしょ。これって1回にまとめられないかな。 いや、別に2回処理やっても良いんだけど、 「3じゃなくて4に...

      • #include &lt;stdio.h&gt;int main(){ int i, j = 0; for(i=1; i&lt;=100; j = 0, i++){ if(i % 3 == 0) { printf(&quot;Fizz&quot;); j = 1; } if(i % 5 == 0) { printf(&quot;Buzz&quot;); j = 1; } if (j) { printf(&quot;&#92;n&quot...

        • フラグを導入するっていうのは一つの手だけど 今度はプログラム中でフラグを管理する必要が出てくるじゃない。 プログラムの中で管理する「状態」は極力減らせっていう鉄則 (何に書...

          • じゃぁ再帰で。 int fizz_buzz(int i, int j) { if(i % 3 == 0 &amp;amp;&amp;amp; j % 3 != 0) { printf(&quot;Fizz&quot;); return 1+ fizz_buzz(i / 3, j * 3); } if(i % 5 == 0 &amp;amp;&amp;amp; j % 5 != 0) { printf(&quot;Buzz&quot;); r...

            • さっきのはあまりに汚かったのでちゃんと書いたよ!! static int do_fizz_buzz(int num, int rem, const char* str, int print) { if (num % rem == 0) { return do_fizz_buzz(num / rem, rem, str, 1); } if (print) printf(&quot;%...

              • printf("%s", str); printf(str);又はputs(str); "%s"をパースする時間がもったいない とかどうでしょうか?

                • 後者がいいんじゃないかな。前者だと一部コンパイラだとセキュリティ警告が出ると突っ込まれるかもしれない。 この流れだとw

                • Thanks. 確かにそうなんだけど、putsだと勝手に改行が出力されてしまうので一ヶ所しか使えなかった。DRY、forなし、ifなし(:?使ってるのでインチキだけど)、main以外に関数なし風味。これで...

                  • for を再帰で代替するなんて、どういうメリットがあるの?

                    • for を再帰で代替するなんて、どういうメリットがあるの? 逆だよ。ループ構文が再帰の一種なの。 だから代替するってのはちょっと違う。

                      • ループが再帰処理の一種・・・? 再帰処理って処理を停止して新しい処理を行い、終了後に停止していた処理に戻ることじゃない? 処理A -&gt; 処理B -&gt; 処理A が再帰処理の名前の由来で...

                  • http://anond.hatelabo.jp/20081026002746 ステートマシン大好きっ子としては書かずにいられない もう少しがんばればforも無くせるな fsmの中身ってDRYなの?的な話もあるだろうが,こんなもの他の...

                    • FizzBuzz続き Cにもlambdaがあればいいのに #include &lt;stdio.h&gt;#include &lt;unistd.h&gt;#define FALSE 0#define TRUE !FALSEtypedef struct state_t { int state; int num; int max; int outputted;} state_t;typedef struct fsm_t { ...

                      • なんでここまで簡単な要求を、ここまで複雑に書く必要があるのか、サッパリ理解できません。 それがプログラマというもの? だったら僕はプログラマになんかならなくてもいいや。

                      • じゃ、少しだけ本気出してみたよ。要求仕様にかなうといいんだけど。 #include &lt;stdio.h&gt;int main(void) { puts(&quot;1&quot;); puts(&quot;2&quot;); puts(&quot;Fizz&quot;); puts(&quot;4&quot;); puts(&quot;...

      • 全然DRYじゃないけど、 #include &lt;stdio.h&gt;#define FIZZ 3#define BUZZ 5int main(void){ int i; for(i=1; i&lt;=100; i++){ if((i % FIZZ == 0) || (i % BUZZ == 0)){ if(i % FIZZ == 0) printf(&quot;Fizz&quot;); if(i % BUZZ =...

    • javascript ではこうか、こうでいいのか? window.onload = function() { for(var ix = 0; ix &lt; 100; ix++) { var p = document.createElement(&quot;p&quot;); var text = ix + &quot; : &quot; + fizzBuzz(ix); var textNode = document.cre...

      • いいけど、ifなしforなしfunctionなしで書けたらもっとカコイイ

        • function はなしにできるけど、可読性のためにあったほうがいい。 私なら内部関数にしてでも切り離す。 if なしは以下でできるけど、これは if でやっても変わらないのでは。 text = (counter...

    • プログラマになれません http://anond.hatelabo.jp/20081025202001 今のバイト先の開発チーフには、いつもこの点を注意される。なぜそれでいいのか徹底的に考えろ、ドキュメントをちゃんと読め...

記事への反応(ブックマークコメント)

ログイン ユーザー登録
ようこそ ゲスト さん