web-dev-qa-db-ja.com

ラムダとは何ですか?なぜそれが役立つのですか?

これまでのところ私は聞いた:

  • ラムダ計算
  • ラムダプログラミング
  • ラムダ式
  • ラムダ関数

これはすべて関数型プログラミングに関連しているようです...

どうやらそれはC++ 1xに統合されるので、私はそれを今よりよく理解するかもしれません:

http://en.wikipedia.org/wiki/C%2B%2B0x#Lambda_functions_and_expressions

誰かがラムダのものとは何かを簡単に定義し、それが役立つ場所を与えることができますか?

55
jokoon
  • ラムダ計算

ラムダ計算は、30年代にアロンツォ教会によって発明された計算モデルです。ほとんどの関数型プログラミング言語の構文とセマンティクスは、ラムダ計算に直接または間接的に影響を受けています。

最も基本的な形式のラムダ計算には、抽象化((無名)関数の作成)とアプリケーション(関数の適用)の2つの操作があります。抽象化はλ演算子を使用して実行され、ラムダ計算にその名前が付けられます。

  • ラムダ式
  • ラムダ関数

匿名関数は、「ラムダ」、「ラムダ関数」、または「ラムダ式」と呼ばれることがよくあります。これは、前述のように、λはラムダ計算で匿名関数を作成するための記号でした(そして、Wordのlambdaは、同じ理由でLISPベースの言語)。

  • ラムダプログラミング

これはよく使われる用語ではありませんが、無名関数を使用したプログラミングまたは高階関数を使用したプログラミングを意味すると思います。


C++ 0xのラムダ、それらの動機、および関数ポインターとの関係についてのもう少し詳しい情報(これの多くはおそらくあなたがすでに知っていることの繰り返しですが、ラムダの動機とそれらの違いを説明するのに役立つことを願っています関数ポインタから):

すでにCに存在していた関数ポインタは、たとえば、比較関数をソート関数に渡します。ただし、それらの有用性には制限があります。

たとえば、ベクトルのベクトルを各ベクトルのith要素で並べ替える場合(iはランタイムパラメーターです)、これを関数ポインターで解決することはできません。 2つのベクトルをith要素で比較する関数は3つの引数(iと2つのベクトル)を取る必要がありますが、並べ替え関数は2つの引数を取る関数が必要です。必要なのは、何らかの方法で引数iを関数に渡してから、それを並べ替え関数に渡す方法ですが、プレーンなC関数ではこれを行うことはできません。

これを解決するために、C++は「関数オブジェクト」または「ファンクター」の概念を導入しました。ファンクタは基本的にoperator()メソッドを持つオブジェクトです。これで、クラスCompareByIthElementを定義できます。これは、引数iをコンストラクター引数として受け取り、比較する2つのベクトルをoperator()メソッドの引数として受け取ります。 ith要素でベクターのベクターを並べ替えるには、CompareByIthElementを引数としてiオブジェクトを作成し、そのオブジェクトを並べ替え関数に渡すことができます。

関数オブジェクトは単なるオブジェクトであり、技術的な関数ではないので(たとえそれらがそのように動作するように意図されていても)、関数ポインターを関数オブジェクトにポイントすることはできません(もちろん、関数オブジェクトへのポインターを持つことはできますが、 _CompareByIthElement*_のような型を持つため、関数ポインターではありません)。

関数を引数として取るC++標準ライブラリのほとんどの関数は、関数ポインターだけでなく関数オブジェクトでも機能するようにテンプレートを使用して定義されます。

今ラムダに:

クラス全体を定義してith要素で比較することは、ベクトルをソートするために一度だけ使用する場合は少し冗長です。関数ポインターのみが必要な場合でも、名前付き関数の定義は、a)ネームスペースを汚染し、b)関数は通常非常に小さく、実際には存在しないため、一度しか使用されない場合は最適とは言えません。ロジックをそれ自体の関数に抽象化する正当な理由(関数を定義せずに関数ポインターを持つことはできません)。

したがって、このラムダを修正するために導入されました。ラムダは関数オブジェクトであり、関数ポインタではありません。 [x1, x2](y1,y2){bla}のようなラムダリテラルを使用すると、基本的に次のことを行うコードが生成されます。

  1. 2つのメンバー変数(_x1_および_x2_)と、引数(_y1_および_y2_)および本体blaを持つoperator()を持つクラスを定義します。
  2. クラスのインスタンスを作成し、メンバー変数_x1_および_x2_を現在スコープ内にある変数_x1_および_x2_の値に設定します。

したがって、ラムダは、ラムダを使用する以外の方法でラムダを実装するために生成されたクラスにアクセスできないことを除いて、関数オブジェクトのように動作します。したがって、ファンクタを引数として受け入れる関数(基本的には標準ライブラリの非C関数を意味します)はラムダを受け入れますが、関数ポインタのみを受け入れる関数は受け入れません。

43
sepp2k

基本的に、ラムダ関数は「オンザフライ」で作成する関数です。 C++ 1xでは、関数型プログラミングのサポートを改善するために使用できます。

_std::for_each( begin, end, [](int i){std::cout << i << '\n';} );
_

これにより、おおよそ次のようなコードが生成されます。

_struct some_functor {
  void operator()(int i) {std::cout << i << '\n';}
};

std::for_each( begin, end, some_functor() );
_

std::for_each()へのこの1回の呼び出しだけで_some_functor_が必要な場合、そのラムダ関数にはいくつかの利点があります。

  • ループで何が行われるかは、ループ関数が呼び出される場所で指定されます
  • それはあなたがボイラープレートコードのいくつかを書くことからあなたを解放します
  • 何が必要なのかと疑問に思うコードを誰もが見るようにする名前空間スコープにあるファンクターはありません
18
sbi

ラムダ関数は、無名関数の別の名前です-基本的に名前のない関数です。

通常、これは関数を1回だけ使用する必要がある言語で使用します。たとえばの代わりに

def add(a, b)
  return a+b

そしてその関数を別の関数に渡す

reduce(add, [5,3,2])

ラムダを使用すると、単純に

reduce(lambda x, y: a+b, [5,3,2])
7
Martin Konecny