Python の lambda もどき(無名関数)
(2009/04/29 エントリのレイアウトを修正)
はじめに
自宅PCのフォルダを漁っていたら,2006年1月6日にmixiの日記へ掲載した,C++でlambda(無名関数)もどきの機能を実現しようとしていたコードを発見しました.
この機能,別に作る必要はないと思います.ただ,面白そうなのでやってみた,ということだったのでしょう(→当時の私).
機能の紹介
lambda機能を使用すると,
001 int r = (2*_X/3+1-2)(12);
このようにかけます._Xの部分に12が代入されると思ってください.
ちなみに上記の式は,
001 int equ(int x) { return 2*3/x + 1 - 2; } 002 int r = equ(12);
と同じことをします.
この機能を使用することで,
001 std::vector<int> v; 00i ... 00n std::transform(v.begin(), v.end(), v.begin(), 2*_X+3);
のように,本来ならば関数か関数オブジェクトを定義し,引数に渡さなければならないところに,式をそのまま記述することができるようになります(“2*_X+3”の部分です).
問題発生
「そういえば,こんなの作ったなぁ.」と思いながらざっと眺めてみたところ,早速バグを発見...我ながら情けない.(^_^;
001 r = (2*3/_X+1-2)(12);
上記の式を実行すると,本来ならば結果としてr==-1となるはずですが(←intの演算結果です),除算における被除数と除数との関係にバグがあったので,r==1になってしまっていました.
出来上がったもの
バグ修正のついでに,単項演算子を追加しました.
template< typename T > struct lambda_default { const T operator()( const T& v, const T& ) const { return v; } }; template< typename T > struct dummy_lambda { const T operator()( const T& v ) const { return v; } }; template < typename T, class PL=dummy_lambda<T>, class OPE = lambda_default<T> > class lambda { public: lambda( const PL& pl=PL(), const OPE& ope=OPE(), const T& v=T() ) : m_pl(pl), m_ope(ope), m_v(v) {} const T operator()( const T& v ) const { return m_ope(m_pl(v), m_v); } private: OPE m_ope; PL m_pl; T m_v; }; #define lambda_unary_operator(id, ope) \ template< typename T > struct lambda_##id { \ const T operator()( const T& a, const T& ) const \ { return ope a; } \ }; \ template< typename T, class PL, class OP > \ lambda<T, lambda<T,PL,OP>, lambda_##id<T> > operator ope ( const lambda<T,PL,OP>& l ) \ { return lambda<T, lambda<T,PL,OP>, lambda_##id<T> >(l, lambda_##id<T>(), T()); } lambda_unary_operator(negate, -) lambda_unary_operator(logical_not, !) #define lambda_binary_operator(id, ope) \ template< typename T > struct lambda_##id { \ const T operator()( const T& a, const T& b ) const \ { return a ope b; } \ }; \ template< typename T > struct lambda_pm_##id { \ const T operator()( const T& a, const T& b ) const \ { return b ope a; } \ }; \ template< typename T, class PL, class OP > \ lambda<T, lambda<T,PL,OP>, lambda_##id<T> > operator ope ( const lambda<T,PL,OP>& l, const T& v ) \ { return lambda<T, lambda<T,PL,OP>, lambda_##id<T> >(l, lambda_##id<T>(), v); } \ template< typename T, class PL, class OP > \ lambda<T, lambda<T,PL,OP>, lambda_pm_##id<T> > operator ope ( const T& v, const lambda<T,PL,OP>& l ) \ { return lambda<T, lambda<T,PL,OP>, lambda_pm_##id<T> >(l, lambda_pm_##id<T>(), v); } lambda_binary_operator(plus, +) lambda_binary_operator(minus, -) lambda_binary_operator(multiplies, *) lambda_binary_operator(divides, /) const lambda<int> _X; const lambda<bool> _B;
演算子定義をマクロ化することで,演算子の追加を簡単にしています(lambda_unary_operatorとlambda_binary_operator).
PLが1つ前のlambdaオブジェクト,OPE(演算子定義内ではOP)がその時点での演算functorです.
例えば,(2*_X+3)であれば,
((2*_X)+3)
(+ (* 2 _X) 3)
(plus (multiplies 2 _X) 3)
(multiplies 2 _X)がPLにあたり,その評価結果をresとすれば,
(plus res 3)が class lambda 中にある,m_ope(m_pl(v), m_v) に対応することになります.
こんな感じで演算自体をPLにどんどん詰め込んでいき,最後に(2*_X+3)(3)とすることで,一気に計算します.