時代遅れひとりFizzBuzz祭り、今回はC++。前回がC言語だったのでその流れでC++、であると同時に、C言語に次いで仕事でよく使う言語だから、という理由もある。
C++は苦手だ。機能が多くて且つ複雑怪奇な所が多々あるあたりが苦手だ。しかし機能が多いということは便利機能もそれなりにある(かもしれない)ということで、自分が分かる範囲でそれらの機能を使う分には非常に便利だ。C++の標準ライブラリはPerlやRubyの組み込み機能と比べると見劣りするけど、C言語と比べれば遥かに充実している。
ただ、基本的に複雑怪奇な言語なので「自分が分かる範囲」の知識すら正しいかどうか怪しかったりする。C++を使う度に、常に疑心暗鬼に駆られてしまう。
それはともかく、FizzBuzzだ。C++を本格的に勉強したことがないので、better Cで書いている。
#include <cassert> #include <cstdlib> #include <iostream> #include <sstream> static void pfizbiz(int n) { assert(n > 0); std::ostringstream oss; if (n % 3 == 0) oss << "Fizz"; if (n % 5 == 0) oss << "Buzz"; if (oss.str().empty()) oss << n; std::cout << oss.str() << std::endl; } int main() { int i; for (i = 1; i <= 100; ++i) { pfizbiz(i); } return EXIT_SUCCESS; }
基本的な発想はC言語の時と同じで、コードもそれ程変わっていない。あえて違いを挙げるなら、ostringstreamを使っている点ぐらいだ。
ところでC++にはC言語よりも便利な標準ライブラリがあって、現行の規格では入出力や文字列、先ほど挙げたstringstream、そしてSTLが含まれている。
STLというと何故かvectorに始まるコンテナの類やイテレータの印象が強いのだけど、それ以外にコンテナを汎用的に操作できるアルゴリズムや、そのカスタマイズに使える関数オブジェクトが含まれている。
で、STLのアルゴリズムや関数オブジェクトあたりに手を出すようになると、従来の古典的C++スタイルとは異なる世界が見えてくる、と思う。関数型プログラミング的なアプローチも限定的ながら可能だ。
ということで、STLで少し遊んでみたバージョンのFizzBuzzを書いてみた。
#include <cassert> #include <cstdlib> #include <algorithm> #include <iostream> #include <iterator> #include <sstream> #include <string> #include <vector> class countfrom { public: countfrom(int n) : count(n) {} int operator()() { return count++; } private: int count; }; struct to_s { std::string operator()(int n) const { assert(n > 0); std::ostringstream oss; if (n % 3 == 0) oss << "Fizz"; if (n % 5 == 0) oss << "Buzz"; if (oss.str().empty()) oss << n; return oss.str(); } }; struct print : public std::binary_function<std::ostream *, std::string, void> { void operator()(std::ostream *out, std::string s) const { assert(out != NULL); *out << s << std::endl; } }; int main() { try { std::vector<int> tmp(100); std::generate_n(tmp.begin(), 100, countfrom(1)); std::vector<std::string> result(100); std::transform(tmp.begin(), tmp.end(), result.begin(), to_s()); std::for_each(result.begin(), result.end(), std::bind1st(print(), &std::cout)); } catch (...) { return EXIT_FAILURE; } return EXIT_SUCCESS; }
個人的には関数オブジェクトを関数内で定義したいのだが、手元のgccではダメだった。言語仕様的にNGなのか処理系側の問題なのかは、調べてないので不明。いい加減、言語仕様面の詳細まで書かれている本を買うべきなんだろうなあ。