時代遅れひとりFizzBuzz祭り C++編

時代遅れひとりFizzBuzz祭り、今回はC++。前回がC言語だったのでその流れでC++、であると同時に、C言語に次いで仕事でよく使う言語だから、という理由もある。

C++は苦手だ。機能が多くて且つ複雑怪奇な所が多々あるあたりが苦手だ。しかし機能が多いということは便利機能もそれなりにある(かもしれない)ということで、自分が分かる範囲でそれらの機能を使う分には非常に便利だ。C++の標準ライブラリはPerlRubyの組み込み機能と比べると見劣りするけど、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なのか処理系側の問題なのかは、調べてないので不明。いい加減、言語仕様面の詳細まで書かれている本を買うべきなんだろうなあ。