誰もが話し続けるこの「ラムダ」とは何ですか?


93

誰もが話し続けるこの「ラムダ」とは何ですか?多くの人がそれを気に入っているようですが、そこから収集できるのは、たくさんのコード行を単一の式に詰め込む方法にすぎません。

誰かがその真の価値について教えてくれませんか?


16
私は、質問者がどこにも.NET言及していないことを回答者に指摘することができます


ひび割れ質問。質問してくれてありがとう。
ピーナッツ、

ラムダは関数型プログラミング(宣言型プログラミング)の世界に属しています。
RBT

回答:


179

名前のない関数

簡単に言えば、ラムダは名前のない関数、または無名関数です。実行可能コードの小さな断片。変数のように渡すことができます。JavaScriptの場合:

function () {}; // very simple

これらのラムダのいくつかの用途を見てみましょう。

ボイラープレートコードの抽象化

ラムダは、定型コードを抽象化するために使用できます。たとえばループ。私たちは一日中、書き込みforwhileループに慣れています。しかし、これは書かれていないコードです。ループの最も重要な部分であるループ内のコードを抽出し、残りを抽象化できます。

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

forEach配列オブジェクトのを使用すると、次のようになります。

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

上記の抽象化はそれほど有用ではないかもしれませんが、forEachはるかに有用なタスクを実行する、などの他の高次関数があります。例filter

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

コード実行遅延

イベントの概念が利用可能な一部の環境では、ラムダを使用して、ある時点で発生する可能性のあるイベントに応答できます。

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

これは他の方法で行うこともできますが、かなり冗長です。たとえば、JavaにはRunnableインターフェースがあります。

機能の工場

この時点まで、構文糖度機能にはラムダのみを使用していました。しかし、ラムダの方がはるかに便利な場合があります。たとえば、ラムダを返す関数があるとします。戻り値をキャッシュしたい関数があるとしましょう。

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

後で、同じような機能があることに気付くでしょう:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

そこには明らかにパターンがあるので、抽象化してみましょう。メモ化を使ってみましょう。

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

ご覧のとおり、ラムダを使用することで、キャッシング/メモ化ロジックを抽象化することができました。他の例でいくつかの回避策があった場合、この特定の問題は他の手法を使用してもほとんど解決されないと思います。重要なボイラープレートコードを1か所に抽出できました。言うまでもなく、usersphotosグローバル変数を削除しました。

あなたのプロフィールを見ると、あなたはほとんどPythonユーザーであることがわかります。上記のパターンの場合、Pythonにはデコレーターの概念があります。ネット上には、メモ化デコレータのがたくさんあります。唯一の違いは、Pythonではデコレータ関数内に名前付きのネストされた関数がある可能性が最も高いことです。その理由は、Pythonが単一式のラムダのみをサポートしているためです。しかし、コンセプトは同じです。

Pythonラムダの使用例として。偶数をフィルター処理した上記のコードは、次のようにPythonで表すことができます。

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

とにかく、ラムダはクロージャーなしではそれほど強力ではありません。クロージャはラムダの概念を非常に強力にするものです。私のメモ化の例では、クロージャーを使用してパラメーターの周りにクロージャーを作成しましたstore。このようにして、memoize関数が結果(ラムダ)を返した後でも、そのパラメーターにアクセスできます。


3
すごい時間をかけましたね。
mk12 2009

4
@ Mk12、実際の答えではなく、
そういう

良い答えですが、「Javaの観点から」「機能的なインターフェース」に関する情報が欠けています。
djangofan 2013

「抽象ボイラープレートコード」の「===」演算子は何ですか?
Don


19

「ラムダ」という用語は、匿名関数、通常はクロージャーを指すために使用されます。コードを不必要に肥大化させることなく、他の関数を使用する関数を記述できるので、これらは便利です。たとえば、Rubyでは:

(1..100).select {|num| num % 2 == 0}

これにより、1〜100の偶数を含む配列が作成されます。明示的なループを記述する必要はありません。selectメソッドは、値をテストするために使用する関数を受け取るため、必要なのはカスタムロジックだけです。これにより、ほとんど手間やオーバーヘッドをかけずにメソッドを大幅にカスタマイズできます。基本的に、小さな関数から関数を作成できます。

それは彼らができることの簡単な例にすぎません。関数をデータとして渡す機能は非常に強力であり、関数型言語のプログラマーは日常的にそれを使用していくつかの本当に驚くべきことを行います。


6
たぶん、パイプ内のものはパラメータであることを追加する必要があります。私はルビーを読むのに苦労しているこれらの人々の一人です。
Skurmedel 2009

これはいい答えです。Ionutが私の票を得た唯一の理由は、ラムダについて(詳細に)気にする必要がある理由を彼が私たちに言ったことです。
フランク・シアラー2009

ラムダが通常はクロージャであることに私は同意しません。
jwg 2013年

6

「ラムダ」は少なすぎるかもしれません。ラムダ計算を見てください。関数型プログラミングで役立ちます。

また、関数型プログラミングは、もう1つのプログラミングパラダイムです(手続き型またはオブジェクト指向など)。


5
それから人々は「ラムダ」について話します、おそらく彼らは匿名関数、関数ポインタ、クロージャーまたは類似の何かを話しているでしょう。ほとんどの場合、実際のラムダ計算については言及しません。
J-16 SDiZ 2009

ラムダ計算について言及してくれてうれしいです。+1。
RBT

5

.NETのラムダは、「構文糖」と呼ばれることがよくあります。それらは機能に直接影響しませんが、人々が言語を使いやすくします。

それらを使用する力を理解すると、デリゲート/匿名メソッドを使用する古いスタイルの方法と比較して、少ないコードを書くことがわかるでしょう。


1
私は誰もが.NETについて言及したとは思わないので、OPはおそらくより一般的な答えでより良いでしょう。
molf 2009

2
それが.netについての回答で明確にした理由です。他の人が自分の言語実装を思いついた場合、Q&Aは言語の選択に関係なく多くの人々を助けます。
redsquare 2009

これは、ラムダについて何か聞いたことがあるようですが、あなた自身はまだそれらを理解していません。
jwg 2013年

2

ドブスジャーナル博士には、ラムダ式を紹介する有用な記事があります(C ++のコンテキスト内ですが、原則を任意の言語に適用できます)。

記事が言うように:「ラムダ式は、個別のクラス/関数定義を必要としない非常にコンパクトな式です。」

したがって、DDJのリスト1と2の例を書く代わりに使用します。

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

次のような個別のクラス定義が必要です。

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

Boostラムダライブラリを使用すると、次のようになります。

std::for_each(vec.begin(),vec.end(),std::cout << _1);

これはインラインで定義を保持します。

また、ラムダ式のいくつかのアプリケーションについても説明します。

DDJ記事の重要なポイントは「通常、ラムダ式は、呼び出しサイトで小さくて複雑すぎない関数が必要な場合に使用されます。関数が自明ではない場合、ラムダ式ではなく通常の関数または関数オブジェクト。」


2

関数ポインター、デリゲート、ストラテジー、またはオブザーバーパターン/イベント処理を使用する関数/メソッドを使用していて、「この関数全体を1回だけ使用するために記述している-このメソッドに渡すため」 ;コードを乱雑にするのではなく、適切な場所に記述できればいいのにと思います。 "-ここでLambda関数を使用できます。この構造をサポートする言語は、通常、特にリスト(ファーストクラス関数および高次関数)の操作に関して、関数をパラメーターとして渡すという概念を大幅に利用します。これは、計算のためにメモリの変更ではなく関数の構成に依存する関数型言語に特に当てはまります。場合によっては(Pythonなどの言語)、


2

「ラムダ」という言葉は、コンピュータサイエンスのタイプが、コンピュータサイエンスの学位を取得するよりも数学やロジックでトレーニングされる可能性が高かった時代からの用語です。それらの中には、「関数型プログラミング」と呼ばれるパラダイムを作り上げました。これは、命令型とはかなり異なり、非常に強力でもあります。AFAIKは、この用語が使用されるようになった環境です。

数学者と論理学者は奇妙な言葉を使うことに慣れています。

「ラムダ」は本当に難解に聞こえます-まるでそれらが非常に奇妙で特別なものであるかのように。実際、Webブラウザーアプリケーション用のJavaScriptを記述し、 "var foo = function(){...}"イディオムを使用する場合、ずっとラムダ関数を使用してきました。


1

ラムダ式は関数の単純な形式です。考え方は、左側のフォームの一部(パラメーターと同等)が右側のフォームの一部(本体と同等)になるというものです。

たとえば、cシャープでは:

x => x * x

値を二乗するラムダです。形の何か

x

形になる

x * x

0

「ラムダ式は、式とステートメントを含むことができる無名関数であり、デリゲートまたは式ツリータイプを作成するために使用できます。

すべてのラムダ式は、ラムダ演算子=>を使用します。これは、「移動先」として読み取られます。ラムダ演算子の左側は入力パラメーター(ある場合)を指定し、右側は式またはステートメントブロックを保持します。ラムダ式x => x * xは、「xはxのx倍になります」と読み取られます。

MSDNから


4
ええ、マイクロソフトは誰もが彼らがそれを発明したと思い込ませます。ただし、ラムダ式はマイクロソフトよりも古いものです。これは、いくつかのプログラミング言語に適用されている数学用語です。(これは可能です。数学自体がコンピューター言語と見なされる可能性があるためです。)
Wim ten Brink

4
これはMicrosoftの.Net実装に固有のものであることに注意してください。ラムダの一般的な考えからの大きな逸脱ではありませんが、Lispで実装されている機能はより「標準的」だと思います。
チャックは

2
その答えは、ラムダが何であるか(匿名関数、デリゲート、式ツリー型の定義が必要)をまったく説明しておらず、その値が何であるかを説明していません。
danio

0

ラムダ式の詳細については、ウィキペディアもご覧ください。(ラムダ計算とプログラミング言語の部分までスクロールダウンします。)ラムダ式は新しいものではなく、C#の一部ではなく、ほぼ80年前にコンピューティングに導入されたものです!ラムダ式は関数型プログラミングの基礎です。

それは価値がありますか?まあ、それは実際にはかなり古いことを考えると、私は言うでしょう:計算を行う誰にとっても非常に価値があります。


0

Javaに慣れている方は、この機能をJava 7に追加するためのさまざまな提案があったため、過去2か月でラムダまたはクロージャーについて多くのことを耳にしました。提案の1つはNeal Gafterからのものであり、ここで詳細に説明されています:javac.info。これは、ユースケースと利点(特に内部クラスに対する利点)を理解するのに役立ちました




-1

はい。これは、多数のコード行を1つの式に詰め込む方法にすぎません。しかし、このような効率的な詰め込みにより、プログラムを構造化するいくつかの新しい方法が可能になります。

単一の式に対して新しい関数またはクラスを宣言するのが面倒なので、デリゲートまたはコールバックの記述を避け、手続き型のスタイルに戻ることがよくあります。

ラムダ式を使用すると、最も小さなタスクでもコールバックを使用する価値があり、コードがより明確になる場合があります。ではないかもしれない。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.