「閉鎖」と「ラムダ」の違いは何ですか?


811

誰かが説明できますか?私はそれらの背後にある基本的な概念を理解していますが、それらが同じ意味で使用されているのをよく見、混乱しています。

ここに来たので、通常の関数とどう違うのですか?


85
ラムダは言語構成要素(無名関数)であり、クロージャーは(無名かどうかにかかわらず)ファーストクラスの関数を実装するための実装手法です。残念ながら、これは多くの人によってしばしば混乱しています。
Andreas Rossberg、



PHPクロージャーについては、php.net/manual/en/class.closure.phpを参照してください。JavaScriptプログラマが期待するものではありません。
PaulH 2016年

SasQの答えは素晴らしいです。私見この質問は、視聴者をその回答に導いた場合、SOユーザーにとってより役立つでしょう。
AmigoNico

回答:


703

ラムダはなし名前で定義された関数-ちょうど無名関数です。Schemeなどの一部の言語では、名前付き関数と同等です。実際、関数定義は、ラムダを変数に内部的にバインドするように書き直されています。Pythonのような他の言語では、それらの間にいくつかの(かなり不必要な)違いがありますが、それ以外は同じように動作します。

クロージャは任意の関数である上に閉じた環境、それが定義されました。つまり、パラメーターリストにない変数にアクセスできます。例:

def func(): return h
def anotherfunc(h):
   return func()

- が未定義であるため、環境が閉じfuncないため、エラーが発生します。地球環境のみを閉じます。これは動作します:anotherfunchfunc

def anotherfunc(h):
    def func(): return h
    return func()

ここで、funcanotherfunc、およびpython 2.3以降(またはこのようないくつかの数)で定義されているため、クロージャがほぼ正しくなった場合(突然変異はまだ機能しません)、これはの環境を閉じて anotherfunc内部の変数にアクセスできることを意味しますそれ。Python 3.1以降では、nonlocalキーワードを使用するとミューテーションも機能します。

もう1つの重要な点funcは、anotherfuncで評価されなくなった場合でも、の環境は引き続き閉じられanotherfuncます。このコードも機能します:

def anotherfunc(h):
    def func(): return h
    return func

print anotherfunc(10)()

これは10を印刷します。

お気づきのように、これはラムダとは何の関係もありません。これらは2つの異なる(関連していますが)概念です。


1
Claudiu、私の不確かな知識では、Pythonはクロージャーを正しく修正したことがありません。私が見ていなかったときに、それらは可変性の問題を修正しましたか?かなり可能です...
サイモン

サイモン-あなたは正しい、彼らはまだ完全に正しいわけではない。私はpython 3.0がこれを回避するためのハックを追加すると思います。(「非ローカル」識別子のようなもの)。私はそれを反映するように私のansewrを変更します。
Claudiu

2
@AlexanderOrlov:ラムダとクロージャーの両方です。以前は、匿名の内部クラスを介してJavaにクロージャーがありました。その機能は、ラムダ式を使用して構文的に簡単になりました。したがって、おそらく新機能の最も重要な側面は、ラムダが存在することです。それらをラムダと呼ぶのは間違いではありません、それらは確かにラムダです。Java 8の作者がクロージャーであるという事実を強調しないことを選択する理由は、私が知っているものではありません。
Claudiu、2015

2
@AlexanderOrlov Java 8ラムダは真のクロージャーではないため、クロージャーのシミュレーションです。それらはPython 2.3クロージャーに似ており(変更不可、したがって、参照される変数の要件は「実質的に最終的」である必要があります)、内部スコープで、囲んでいるスコープで参照されるすべての変数を非表示パラメーターとして受け取る非クロージャー関数にコンパイルされます。
ローガンピックアップ

7
@Claudiu特定の言語実装(Python)への参照は、答えを複雑にしすぎると思います。質問は完全に言語に依存しません(言語固有のタグもありません)。
マタイ

318

ここのこのStackOverflowの質問への回答でさえ、ラムダとクロージャーに関して多くの混乱があります。特定のプログラミング言語または他の無知なプログラマーの実践からクロージャーについて学んだランダムなプログラマーに尋ねる代わりに、ソース(すべてが始まったところ)への旅に出ます。そしてラムダとクロージャは、最初の電子コンピュータが存在する前の30年代にAlonzo Churchによって発明されたラムダ計算から来ているので、これが私が話している情報です。

ラムダ計算は世界で最も単純なプログラミング言語です。あなたがそれにできる唯一のこと:►

  • アプリケーション:ある式を別の式に適用しf xます。
    (それは関数呼び出しと考えてください。ここfで、は関数でxあり、唯一のパラメーターです)
  • 要約:式で発生するシンボルをバインドして、このシンボルが単なる「スロット」、値が入力されるのを待機している空白のボックス、つまり「変数」であることを示します。これは、ギリシャ文字λ(ラムダ)、記号名(例x:).、式の前にドットを付加することによって行われます。次に、式を1つのパラメータを必要とする関数に変換します
    例:λx.x+2x+2を受け取り、xこの式のシンボルがバインドされた変数であることを通知します。これは、パラメーターとして指定した値で置き換えることができます。
    この方法で定義された関数は匿名であることに注意してください–名前がないため、まだ参照することはできませんが、次のように、待機しているパラメーターを指定することで、すぐに呼び出すことができます(アプリケーションを覚えていますか?)(λx.x+2) 7。次に、式(この場合はリテラル値)7が、適用されたラムダのx部分式x+2のように置き換えられるため、が得られます7+2。これは9、一般的な算術規則によってに縮小されます。

そこで、謎の1つを解決しました
。lambdaは、上の例の無名関数λx.x+2です。


異なるプログラミング言語では、関数の抽象化(ラムダ)の構文が異なる場合があります。たとえば、JavaScriptでは次のようになります。

function(x) { return x+2; }

そして、あなたはすぐにこのようないくつかのパラメータにそれを適用することができます:

(function(x) { return x+2; })(7)

または、この無名関数(lambda)をいくつかの変数に格納できます。

var f = function(x) { return x+2; }

これは事実上それに名前を付けf、それを参照して後で何度も呼び出すことができるようにします。例:

alert(  f(7) + f(10)  );   // should print 21 in the message box

しかし、あなたはそれに名前を付ける必要はありませんでした。あなたはそれをすぐに呼び出すことができます:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

LISPでは、ラムダは次のように作成されます。

(lambda (x) (+ x 2))

そして、そのようなラムダをパラメータにすぐに適用することで呼び出すことができます:

(  (lambda (x) (+ x 2))  7  )


OK、それではもう1つの謎を解く時間です。クロージャーとは何ですか。それを行うために、ラムダ式のシンボル変数)について話しましょう。

私が言ったように、ラムダ抽象化が行うことは、その副次式でシンボルをバインドすることです。それにより、それは置換可能なパラメータになります。このようなシンボルは、バインドと呼ばます。しかし、式に他の記号がある場合はどうなりますか?次に例を示しますλx.x/y+2。この式では、シンボルxはそのλx.前のラムダ抽象化によってバインドされています。しかし、もう1つの記号yは拘束されていません。自由です。それが何でどこから来たのかわからないので、それが何を意味し、どのようなを表しているのかわからないため、意味を理解するまでその式を評価することはできませんy

実際、同じことが他の2つの記号、2およびにも当てはまり+ます。これらの2つの記号に慣れ親しんでいるだけで、コンピューターがそれらを知らないことを忘れてしまいます。たとえば、ライブラリや言語自体のどこかに定義することで、それらの意味を伝える必要があります。

フリーシンボルは、式とは別に、「環境」と呼ばれる「周囲のコンテキスト」で定義されていると考えることができます。環境は、この式の一部であるより大きな式かもしれません(Qui-Gon Jinnが言ったように、「常により大きな魚がいます」;))、またはいくつかのライブラリー、または言語自体(プリミティブとして)。

これにより、ラムダ式を2つのカテゴリに分割できます。

  • CLOSED式:これらの式で発生するすべてのシンボルは、ラムダ抽象化によってバインドされます。言い換えれば、それらは自己完結型です。評価のために周囲のコンテキストを必要としません。それらはコンビネータとも呼ばれます
  • OPEN式:これらの式の一部のシンボルはバインドされていません。つまり、これらの式に含まれる一部のシンボルは解放されており、外部情報を必要とするため、これらのシンボルの定義を提供するまで評価できません。

これらのフリーシンボルをいくつかの値(数値、文字列、匿名関数、別名ラムダなど)にバインドすることで定義する環境を提供することにより、オープンラムダ式を閉じることができます。

そして、ここでクロージャ部分が来ます:ラムダ式
クロージャは、この式のフリーシンボルに値を与え、もはやフリーではないようにする、外部コンテキスト(環境)で定義されたこの特定のシンボルのセットです。それはターンオープンに、まだいくつかの「未定義」の自由のシンボルが含まれているラムダ式を、閉じもはや任意の自由のシンボルを持っていないもの、。

たとえば、次のラムダ式を持っている場合:λx.x/y+2、シンボルはxシンボルながら、バインドされているy自由である、したがって、式がありopen、あなたが何を言っていない限り、評価することはできませんy(と同じで手段+2も自由です、)。しかし、次のような環境もあるとします。

{  y: 3,
+: [built-in addition],
2: [built-in number],
q: 42,
w: 5  }

この環境当社ラムダ式から全ての「未定義」(無料)のシンボルのための消耗品の定義(y+2)、およびいくつかの余分な記号(qw)。定義する必要があるシンボルは、環境のこのサブセットです。

{  y: 3,
+: [built-in addition],
2: [built-in number]  }

そしてこれはまさにラムダ式の閉鎖です:>

つまり、開いているラムダ式を閉じます。これは、名前のクロージャが最初に由来した場所であり、これが、このスレッドでの非常に多くの人々の回答がまったく正しくない理由です:P


なぜ彼らは間違っているのですか?クロージャーがメモリ内のデータ構造、または使用する言語の機能であるとなぜ多くの人が言うのか、なぜクロージャーとラムダを混同するのか?:P

それは、Sun / Oracle、Microsoft、Googleなどの企業マーケットイドが非難されるべきです。それは、言語(Java、C#、Goなど)でこれらの構成体を呼んだためです。彼らはしばしばラムダであるはずのものを「閉鎖」と呼びます。または、「クロージャ」を、字句スコープの実装に使用した特定の手法、つまり、関数がその定義時に外部スコープで定義された変数にアクセスできるという事実を呼び出します。多くの場合、関数はこれらの変数を「囲む」、つまり、変数をデータ構造にキャプチャして、外部関数の実行終了後に破棄されないようにします。しかし、これは単なる事後の「民俗語の語源」とマーケティングを作り上げたものであり、混乱を招くだけです。

そして、彼らが言うことには常に少しの真実があり、それをあなたがそれを偽として簡単に却下することができないという事実のために、それはさらに悪いです:P説明させてください:

ラムダをファーストクラスのシチズンとして使用する言語を実装する場合は、周囲のコンテキストで定義されたシンボルを使用できるようにする必要があります(つまり、ラムダで自由変数を使用します)。そして、これらのシンボルは、周囲の関数が戻ったときでも存在している必要があります。問題は、これらのシンボルが関数のローカルストレージ(通常はコールスタック上)にバインドされていることです。これは、関数が戻るときに存在しなくなります。したがって、ラムダが期待どおりに機能するためには、外部コンテキストからこれらのすべての自由変数を何らかの方法で「キャプチャ」し、外部コンテキストがなくなった場合でも後で保存する必要があります。つまり、閉鎖を見つける必要がありますラムダ(それが使用するこれらすべての外部変数)を別の場所に保存します(コピーを作成するか、スタック用以外の場所に前もってそれらのスペースを準備します)。この目標を達成するために使用する実際の方法は、言語の「実装の詳細」です。ここで重要なのは、どこかに保存する必要があるラムダの環境からのフリー変数のセットであるクロージャーです。

「クロージャ」自体としてクロージャを実装するために、言語の実装で使用する実際のデータ構造の呼び出しを開始するのにそれほど時間はかかりませんでした。構造は通常、次のようになります。

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

そして、これらのデータ構造はパラメーターとして他の関数に渡され、関数から返され、変数に格納されてラムダを表し、それらの囲い込み環境やそのコンテキストで実行されるマシンコードにアクセスできるようにします。ただし、これクロージャー自体を実装する方法ではなくクロージャーを実装する方法の1つにすぎません。

上記で説明したように、ラムダ式のクロージャは、そのラムダ式に含まれる自由変数に値を与える環境内の定義のサブセットであり、式を効果的に閉じます(まだ評価できない開いたラムダ式を、閉じ、それに含まれるすべてのシンボルが現在定義されているので、その後、評価することができるラムダ式)。

それ以外のものは、これらの概念の本当のルーツを知らない、プログラマーと言語ベンダーの「カーゴカルト」と「ボードゥーマジック」にすぎません。

それがあなたの質問に答えてくれることを願っています。ただし、フォローアップの質問がある場合は、コメントでお気軽に質問してください。詳しく説明します。


50
言語固有ではなく、一般的に物事を説明する最良の回答
Shishir Arora

39
物事を説明するとき、私はこの種のアプローチが大好きです。最初から、物事がどのように機能するかを説明し、次に現在の誤解がどのように作成されたかを説明します。この答えはトップに行く必要があります。
シャーキー

1
@SasQ>ラムダ式のクロージャは、そのラムダ式に含まれる自由変数に値を与える、その環境内の定義のサブセットです。<したがって、データ構造の例では、「ラムダ関数の環境」はクロージャですか?
ジェフM

3
ラムダ計算は私にとって機械語のように感じますが、「作られた」言語とは対照的に「見つけた」言語であることに同意する必要があります。したがって、恣意的な慣習の影響をはるかに受けず、現実の根底にある構造を捉えるのにより適しています。Linq、JavaScript、F#でより具体的でアクセスしやすい仕様を見つけることができますが、Lambda微積分は、気を散らすことなく問題の核心に到達します。
StevePoling

1
何度か言い回しをして、毎回少し異なる言い回しをしていただきありがとうございます。コンセプトの強化に役立ちます。もっと多くの人にやってもらいたいです。
johnklawlor

175

ほとんどの人が関数について考えるとき、彼らは名前付き関数について考えます

function foo() { return "This string is returned from the 'foo' function"; }

もちろん、これらは名前で呼び出されます。

foo(); //returns the string above

ラムダ式、あなたが持つことができ、匿名の機能を

 @foo = lambda() {return "This is returned from a function without a name";}

上記の例では、割り当てられた変数を通じてラムダを呼び出すことができます。

foo();

ただし、匿名関数を変数に割り当てるよりも便利ですが、それらをより高次の関数(つまり、他の関数を受け入れ/返す関数)との間で受け渡します。これらの多くの場合、関数の名前を付ける必要はありません。

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

クロージャは、名前付きまたは匿名関数が、それの機能が定義されているスコープ内の変数「オーバー閉じ」、すなわち、閉鎖はまだで使用されているすべての外の変数と環境を指しますときなどとして知られていることも閉鎖自体。名前付きのクロージャは次のとおりです。

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

それは多くのようには思えませんが、これがすべて別の関数にありincrementX、外部関数に渡された場合はどうなりますか?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

これは、関数型プログラミングでステートフルオブジェクトを取得する方法です。「incrementX」という名前は必要ないため、この場合はラムダを使用できます。

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }

14
ここでは何語ですか?
Claudiu

7
基本的には疑似コードです。その中にはいくつかのlispとJavaScriptがあり、私が設計している "@"( "at")と呼ばれる言語も変数宣言演算子にちなんで名付けられています。
Mark Cidade、

3
@MarkCidade、この言語はどこにありますか?ドキュメントとダウンロードはありますか?
Pacerier 2013

5
Javascriptを使用して、先頭に@記号を付けて変数を宣言する制約を追加しませんか?それは少し時間を節約するでしょう:)
Nemoden

5
@Pacerier:言語の実装を開始しました:github.com/marxidad/At2015
Mark Cidade

54

すべてのクロージャーがラムダであるわけではなく、すべてのラムダがクロージャーであるとは限りません。どちらも関数ですが、必ずしも私たちが慣れ親しんでいる方法ではありません。

ラムダは基本的に、関数を宣言する標準的な方法ではなく、インラインで定義される関数です。ラムダはオブジェクトとして頻繁に渡されます。

クロージャーは、その外部のフィールドを参照することにより、その周囲の状態を囲む関数です。閉じられた状態は、クロージャーの呼び出し全体に残ります。

オブジェクト指向言語では、通常、クロージャはオブジェクトを通じて提供されます。ただし、一部のOO言語(C#など)は、状態を囲むオブジェクトを持たない純粋な関数型言語(lispなど)によって提供されるクロージャの定義に近い特別な機能を実装しています。

興味深いのは、C#でのラムダとクロージャの導入により、関数型プログラミングが主流の使用法に近づいていることです。


では、クロージャーはラムダのサブセットであり、ラムダは関数のサブセットであると言えるでしょうか?
sker

クロージャはラムダのサブセットですが、ラムダは通常の関数よりも特別です。私が言ったように、ラムダはインラインで定義されます。それらが別の関数に渡されるか、戻り値として返されない限り、基本的にそれらを参照する方法はありません。
マイケルブラウン

19
ラムダとクロージャはそれぞれすべての関数のサブセットですが、ラムダとクロージャの交差部分のみがあり、クロージャの交差しない部分はクロージャである名前付き関数であり、交差しないラムダは完全にバインドされた変数。
Mark Cidade、

私の意見では、ラムダは関数よりも基本的な概念です。それは本当にプログラミング言語に依存します。
Wei Qiu 14年

15

ラムダは言語の構成要素です。つまり、匿名関数の構文です。クロージャーはそれを実装するための手法です-あるいは、ファーストネームの関数、つまり名前付きか匿名か。

より正確には、クロージャとは、実行時にファーストクラスの関数がその「コード」とそのコードで使用されるすべての非ローカル変数を「クローズ」する環境のペアとして表される方法です。このようにして、それらの変数は、それらが発生する外部スコープがすでに終了している場合でも、引き続きアクセスできます。

残念ながら、ファーストクラスの値としての関数をサポートしていない、または機能を損なわれた形式でのみサポートしている言語が世の中にたくさんあります。したがって、人々はしばしば「本物」を区別するために「閉鎖」という用語を使用します。


12

プログラミング言語の観点から見ると、これらはまったく異なる2つのものです。

基本的に、チューリング完全言語では、抽象化、適用、削減など、非常に限られた要素のみが必要です。抽象化とアプリケーションは、ラムダ式を構築する方法を提供し、リダクションはラムダ式の意味を決定します。

Lambdaは、計算プロセスを抽象化する方法を提供します。たとえば、2つの数値の合計を計算するには、2つのパラメーターx、yを取り、x + yを返すプロセスを抽象化できます。スキームでは、次のように書くことができます

(lambda (x y) (+ x y))

パラメータの名前は変更できますが、それによって完了するタスクは変更されません。ほとんどすべてのプログラミング言語で、ラムダ式に名前を付けることができます。これは関数と呼ばれます。しかし、それほど大きな違いはありません。それらは概念的には単なる構文糖と見なすことができます。

OK、これをどのように実装できるか想像してみてください。ラムダ式をいくつかの式に適用するときはいつでも、例えば

((lambda (x y) (+ x y)) 2 3)

評価する式でパラメーターを単純に置き換えることができます。このモデルはすでに非常に強力です。ただし、このモデルではシンボルの値を変更できません。たとえば、ステータスの変更を模倣することはできません。したがって、より複雑なモデルが必要です。簡単に言うと、ラムダ式の意味を計算したいときはいつでも、シンボルと対応する値のペアを環境(またはテーブル)に入れます。次に、残りの(+ xy)は、テーブル内の対応するシンボルを検索することによって評価されます。ここで、環境で直接操作するいくつかのプリミティブを提供すると、ステータスの変化をモデル化できます!

この背景で、この機能を確認してください:

(lambda (x y) (+ x y z))

ラムダ式を評価すると、xyが新しいテーブルにバインドされることがわかっています。しかし、どのように、どこでzを検索できるでしょうか。実際には、zは自由変数と呼ばれます。zを含む外部環境が必要です。そうでなければ、式の意味はxとyをバインドするだけでは決定できません。これを明確にするために、スキームで次のように書くことができます:

((lambda (z) (lambda (x y) (+ x y z))) 1)

したがって、zは外部テーブルで1にバインドされます。2つのパラメーターを受け入れる関数も取得しますが、実際の意味は外部環境にも依存します。言い換えれば、外部環境は自由変数で閉じます。set!の助けを借りて、関数をステートフルにすることができます。つまり、それは数学の意味での関数ではありません。それが返すものは、入力だけでなくzにも依存します。

これはあなたがすでによく知っていることであり、オブジェクトのメソッドはほとんど常にオブジェクトの状態に依存しています。そのため、「クロージャは貧乏人のオブジェクト」と言う人もいます。しかし、ファーストクラスの関数が本当に好きなので、オブジェクトを貧乏人のクロージャと見なすこともできます。

私はスキームを使用してアイデアを説明しています。そのスキームは、真の閉鎖性を備えた最も初期の言語の1つです。ここにあるすべての資料は、SICPの第3章でより適切に提示されています。

要約すると、ラムダとクロージャは本当に異なる概念です。ラムダは関数です。クロージャはラムダとラムダを閉じる対応する環境のペアです。


したがって、自由変数が存在しなくなるまで、すべてのクロージャをネストされたラムダで置き換えることができますか?この場合、クロージャーは特別な種類のラムダと見なすことができます。
Trilarion 2014年

8

概念は上記と同じですが、PHPのバックグラウンドを使用している場合は、PHPコードを使用してさらに説明します。

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

function($ v){return $ v> 2; }はラムダ関数の定義です。変数に格納することもできるため、再利用できます。

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

次に、フィルターされた配列で許可される最大数を変更する場合はどうでしょうか。別のラムダ関数を記述するか、クロージャーを作成する必要があります(PHP 5.3)。

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

クロージャは、それ自体の環境で評価される関数であり、関数が呼び出されたときにアクセスできる1つ以上のバインドされた変数を持っています。それらは関数型プログラミングの世界から来ており、そこでは多くのコンセプトが使われています。クロージャーはラムダ関数に似ていますが、クロージャーが定義されている外部環境からの変数と対話する機能を持っているという意味でよりスマートです。

次に、PHPクロージャーの簡単な例を示します。

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

この記事でうまく説明しました。


7

この質問は古く、多くの回答を得ました。
非公式の閉鎖プロジェクトであるJava 8と公式Lambdaを使用して、質問を復活させます。

Javaのコンテキストでの答え(ラムダとクロージャーを使用—違いは何ですか?):

「クロージャーは、それぞれの自由変数を値にバインドする環境と対になっているラムダ式です。Javaでは、ラムダ式はクロージャーによって実装されるため、2つの用語はコミュニティーで交換可能に使用されるようになりました。」


Lamdasは、Javaのクロージャーによってどのように実装されますか?Lamdas式が古いスタイルの匿名クラスに変換されることを意味しますか?
hackjutsu

5

簡単に言えば、クロージャはスコープに関するトリックであり、ラムダは匿名関数です。ラムダによるクロージャをよりエレガントに実現でき、ラムダはより高い関数に渡されるパラメータとしてよく使用されます


4

Lambda式は単なる無名関数です。たとえばプレーンJavaでは、次のように記述できます。

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

ここで、クラスFunctionはJavaコードでビルドされています。これで、mapPersonToJob.apply(person)どこかで呼び出して使用できます。それはほんの一例です。構文が存在する前のラムダです。ラムダスはこのためのショートカットです。

閉鎖:

ラムダは、このスコープ外の変数にアクセスできるときにクロージャーになります。私はあなたがその魔法を言うことができると思います、それはそれが作成された環境を魔法のように包み込み、そのスコープ(外側のスコープ)の外で変数を使用することができます。

Kotlinでは、ラムダは常にそのクロージャー(外部スコープにある変数)にアクセスできます


0

関数が外部変数を使用して操作を実行するかどうかによって異なります。

外部変数 -関数のスコープ外で定義された変数。

  • ラムダ式は、操作を実行するためにパラメーター、内部変数、または定数に依存するため、ステートレスです。

    Function<Integer,Integer> lambda = t -> {
        int n = 2
        return t * n 
    }
    
  • クロージャーは、操作を実行するためにパラメーターと定数と共に外部変数(つまり、関数本体のスコープ外で定義された変数)を使用するため、状態を保持します。

    int n = 2
    
    Function<Integer,Integer> closure = t -> {
        return t * n 
    }
    

Javaはクロージャを作成するときに、変数nを関数とともに保持するため、他の関数に渡したり、どこで使用したりしても参照できます。

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