本の虫: 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::vector v ;
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