本の虫: lambda 完全解説で、lambdaの全機能は、ほぼ網羅したと思う。lambdaの文法は、それほど難しくはないと思うのだが、難しいと感じる人がいるらしい。とくに、キャプチャが理解できない人がいるそうだ。そこで今回は、lambdaの根本を解説してみようと思う。
lambdaというのは、そもそも関数オブジェクトのシンタックスシュガーなのである。例えば、
namespace hito { template< class InputIterator, class Function > Function for_each( InputIterator first, InputIterator last, Function f ) { for ( ; first != last ; ++first ) f( *first ) ; return f ; } } int main() { std::vector<int> v ; for ( int i = 0 ; i != 10 ; ++i ) v.push_back(i) ; hito::for_each(v.begin(), v.end(), [](int x){ std::cout << x << std::endl ; } ) ; }
このコードは、これ以上ないくらいに分かりやすいC++のコードである。今、for_eachのしていることを分かりやすくするため、自前で実装してみた。for_eachは、イテレーターに対して、引数として與えられた関数オブジェクトを呼び出すのである。関数オブジェクトは、この場合、lambdaである。
とはいえ、lambdaを理解できない人間は、このコードもまた、理解できないのである。では、以下のように書き換えてはどうか。
struct lambda { void operator ()( int value ) const { std::cout << value << std::endl ; } } ; int main() { std::vector<int> v ; for ( int i = 0 ; i != 10 ; ++i ) v.push_back(i) ; std::for_each(v.begin(), v.end(), lambda() ) ; }
このように関数オブジェクトを渡しているに過ぎないのである。lambdaは所詮、関数オブジェクトのシンタックスシュガーであることが、実感できたことと思う。
しかし、変数のキャプチャはどうか。例えば以下のコード。
int main() { std::vector<int> v ; for ( int i = 0 ; i != 10 ; ++i ) v.push_back(i) ; int sum = 0 ; std::for_each(v.begin(), v.end(), [&]( int value ){ sum += value ; } ) ; std::cout << sum << std::endl ; }
これは実に不可思議である。lambdaが関数オブジェクトであるのはまだしも理解もできようが、なぜ、その関数オブジェクトであるlambdaが、定義された場所の関数の変数を、自分でも使えるのか、理解に苦しむ。こんな奇妙なことが、単なる既存の関数オブジェクトのシンタックスシュガーであるわけがない――と思うかも知れない。
しかし、依然としてlambdaは、関数オブジェクトのシンタックスシュガーなのである。
class lambda { private: int & sum ; public : lambda(int & ref) : sum( ref ) { } void operator ()( int value ) const { sum += value ; } } ; int main() { std::vectorv ; for ( int i = 0 ; i != 10 ; ++i ) v.push_back(i) ; int sum = 0 ; std::for_each(v.begin(), v.end(), lambda( sum ) ) ; std::cout << sum << std::endl ; }
ご覧のように、lambda関数オブジェクトは、コンストラクタの引数で、その関数内の変数を参照に取るだけである。lambdaのキャプチャとは、根本的には、この程度のものなのである。
C++0x以前では、関数オブジェクトは、お世辞にも、活用されているとは言い難かった。既存の関数ポインタより圧倒的に便利なのだが、如何せん、その文法が汚い。上記のlambda関数はいずれも、たった一行の、短いコードである。本質的には一行のコードを書くために、関数オブジェクトは、あまりにも文法上必要なゴミが多い。そのため、使用を敬遠されていたのだ。lambdaなら、その場所に、直に書くことが出来る。
lisp、python、javascript、ruby、C#等の言語を話す人からみたら、C++のlambdaは、制限が多いように思われるかも知れない。しかし、強い静的な言語であるC++としては、仕方がないのである。
1 comment:
ラムダ式はhitoさんの説明で十分に理解出来ましたよ
これはいろいろ応用が利きそうですね
Post a Comment