FizzBuzzを書こう!その3 -完結編-

完結・・・?

ようやくやりたいことが出来ました。
といっても、当初とは少し変わってしまいましたが・・・
結局のところ、僕の力量では無理があることがわかりました><

実行結果

・・・は同じだからいいよね? ←

ソース

ということでソースコード

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/bind.hpp>
using namespace std;
using namespace boost::lambda;

int main(){bind(_1,_1,_2)((if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)]),1);}

ついにワンライナーに出来ましたよ!
やったね☆
これでhotたん
http://d.hatena.ne.jp/heisseswasser/20100609/1276056028
に追いつきました!


え?
セミコロンがあるって?

セミコロンレス

セミコロンがあるなら取ってしまえばいいじゃない!
ということで取りました。

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/bind.hpp>
using namespace std;
using namespace boost::lambda;

int main(){if(bind(_1,_1,_2)((if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)]),1),1){}}

単にif文の中に埋めるだけの簡単なお仕事!
ただし、単に移動するだけでは問題があります。
それは解説の方で。

解説

とりあえず整形しましょう。

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/bind.hpp>
using namespace std;
using namespace boost::lambda;

int main( void )
{
	bind(_1,_1,_2) (
		(if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)]),
		1
	);
}

これで少しは読みやすくなった・・・はず! ←


まず最初のbindですが、これはboost::lambda::bindです。
boost::bindではないのでご注意を。

	bind(_1,_1,_2)

これ自体がラムダ式です。
つまり関数オブジェクトです。
どういう関数オブジェクトかというと、引数を2つとり、1つ目の引数である関数(オブジェクト)を、1つ目の引数を第一引数として、2つ目の引数を第二引数として呼び出すラムダ式(関数オブジェクト)です。


さて、ではこのラムダ式に与えられる引数はなにかというと、第一引数は

		(if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)])

で、第二引数は

		1

です。


第一引数はラムダ式です。
はい、またラムダ式です。
FizzBuzzのメインです。
この中身は「FizzBuzzを書こう!」(http://d.hatena.ne.jp/rofi/20100606/1275852609)とほぼ変わりません。
変更点は、第一引数を数字にしていたのを自身の関数オブジェクトを指すものに変え、第二引数を数字にしたことです。
これより、ラムダ式の最後のbindの呼び出しも少し変えてあります。


これより第二引数は数字となります。
ということで、最初の数字である1が引数として与えられてあります。


以上がこのプログラムの全体です。


さて、これをセミコロンレスに書き換えます。
基本的には何も変わりません。
巷でよく使われているテクニックをそのままパクって使わしていただくだけです。


ということで、整形したコードを。

#include <iostream>
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/if.hpp>
#include <boost/lambda/bind.hpp>
using namespace std;
using namespace boost::lambda;

int main( void )
{
	if(
		bind(_1,_1,_2) (
			(if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)]),
			1
		),
		1
	){}
}

#・・・整形になってるかなぁ〜?
先程のコードをそのままif文にいれました。
これでセミコロンは必要ありません。
ただし、

			(if_(_2<=100)[if_(_2%3&&_2%5)[cout<<_2],if_(!(_2%3))[cout<<constant("Fizz")],if_(!(_2%5))[cout<<constant("Buzz")],cout<<constant('\n'),bind(_1,_1,_2+1)])

このFizzBuzzのコードは返り値として何も返しません。
なので、これはvoid型となりif文には入れることが出来ません。
一応boost::lambdaのラムダ式は返り値は適当に返すことが出来ますが、面倒だったので簡単のため順次演算子を用いて,1としました。
これで問題なくセミコロンを取っ払うことが出来ました。
やったね☆

あとがき

これをやりたくて延々と頑張ってました。
しかし、本当にやりたかったのはこれじゃなくもうちょっと使い勝手がいい(?)ものでした。
が、どう頑張っても出来そうにないことがわかったのでここで手を打つことにしました。
本来やりたかったのは擬似的なコードで書くと

	(/* 〜何らかのラムダ式〜 */)(/* 再帰を行ないたいラムダ式 */)(/* 引数 */);

という感じでやりたかったんです。
つまり、再帰を行ないたい関数を入れればあとは引数を渡すだけの関数オブジェクトが作成されるようにしたかったのです。
ラムダ式再帰を行う方法としてYコンビネータがあります。
しかし、C++では(少なくとも私の現在の力では)出来ませんでした。
ある程度型を決めてしまったり関数化してしまうとYコンビネータを作れたりするみたいですが、上記のようにラムダ式のみでやるには曖昧な部分が多すぎるようです。


最後に。
これをやるためにたくさんの試行錯誤を繰り返しました。
Yコンビネータを調べて、同じコードをC++で書いて、ラムダ式に落として云々・・・
結果だけ見るととってもすっきりしちゃいましたが、最初の頃は全く分からず、boost::lambdaとboost::bindが混じった状態でboost::lambda::bindやらboost::bindやらが大量に入り乱れてましたw
そして、試行錯誤してるうちに変なものが出来ました。
面白かったので、簡潔にまとめたものを最後に貼っておきます。

#include <iostream>
#include <boost/lambda/lambda.hpp>
using namespace std;
using namespace boost::lambda;

int main( void )
{
	cout << _1(_1,_2)(_1+_2)(1,2) << endl;
}

実行結果:http://ideone.com/Rrr2Q
#引数に1,2を指定しているのはわざとです ←