ラムダ(関数)とは何ですか?


743

comp-sciの背景がない人にとって、コンピュータサイエンスの世界でのラムダとは何ですか?


3
ここでアムダ式が 美しく説明されました
Jameer Mulani 2017

1
ラムダ関数がさまざまな言語でどのように表示されるか、およびそれらがそれらの言語で導入された年については、これを参照してください。
フィリップシュワルツ

回答:


1081

ラムダはラムダ計算から来ており、プログラミングにおける無名関数を指します。

なぜこれがクールなのですか?名前を付けなくても、使い捨ての関数をすばやく作成できます。また、クロージャーを作成するための優れた方法を提供します。その力であなたはこのようなことをすることができます。

パイソン

def adder(x):
    return lambda y: x + y
add5 = adder(5)
add5(1)
6

Pythonのスニペットからわかるように、関数adderは引数xを受け取り、別の引数yを受け取る無名関数またはラムダを返します。その無名関数を使用すると、関数から関数を作成できます。これは簡単な例ですが、ラムダとクロージャが持つ力を伝えているはずです。

他の言語の例

Perl 5

sub adder {
    my ($x) = @_;
    return sub {
        my ($y) = @_;
        $x + $y
    }
}

my $add5 = adder(5);
print &$add5(1) == 6 ? "ok\n" : "not ok\n";

JavaScript

var adder = function (x) {
    return function (y) {
        return x + y;
    };
};
add5 = adder(5);
add5(1) == 6

JavaScript(ES6)

const adder = x => y => x + y;
add5 = adder(5);
add5(1) == 6

スキーム

(define adder
    (lambda (x)
        (lambda (y)
           (+ x y))))
(define add5
    (adder 5))
(add5 1)
6

C#3.5以降

Func<int, Func<int, int>> adder = 
    (int x) => (int y) => x + y; // `int` declarations optional
Func<int, int> add5 = adder(5);
var add6 = adder(6); // Using implicit typing
Debug.Assert(add5(1) == 6);
Debug.Assert(add6(-1) == 5);

// Closure example
int yEnclosed = 1;
Func<int, int> addWithClosure = 
    (x) => x + yEnclosed;
Debug.Assert(addWithClosure(2) == 3);

迅速

func adder(x: Int) -> (Int) -> Int{
   return { y in x + y }
}
let add5 = adder(5)
add5(1)
6

PHP

$a = 1;
$b = 2;

$lambda = fn () => $a + $b;

echo $lambda();

ハスケル

(\x y -> x + y) 

Javaこの記事を参照してください

// The following is an example of Predicate : 
// a functional interface that takes an argument 
// and returns a boolean primitive type.

Predicate<Integer> pred = x -> x % 2 == 0; // Tests if the parameter is even.
boolean result = pred.test(4); // true

ルア

adder = function(x)
    return function(y)
        return x + y
    end
end
add5 = adder(5)
add5(1) == 6        -- true

コトリン

val pred = { x: Int -> x % 2 == 0 }
val result = pred(4) // true

ルビー

Rubyは、関数を呼び出すのとまったく同じ構文を使用してラムダを呼び出すことができないという点で少し異なりますが、ラムダはまだあります。

def adder(x)
  lambda { |y| x + y }
end
add5 = adder(5)
add5[1] == 6

RubyはRubyなので、ラムダの省略形があるため、次のように定義できますadder

def adder(x)
  -> y { x + y }
end

R

adder <- function(x) {
  function(y) x + y
}
add5 <- adder(5)
add5(1)
#> [1] 6

3
では、ラムダ関数とファンクタの違いは何ですか?
Maxpm 2013年

1
@Maxpmファンクタはインスタンスフィールドと関数を持つ標準オブジェクトにすることができますが、ラムダ関数は通常、1行の命令のみで構成されます。もちろんこれは言語によって異なります。
zdimension 2018

1
ラムダ関数が無名関数と同じであると言うのは必ずしも正確ではないと思います。JavaScriptのような一部の言語では、ラムダ式は匿名関数の特定の形式です。指定したJavaScriptの例はラムダ構文のない無名関数ですが、指定したJavaScript(ES6)の例はラムダ式です。
カイルデラニー

1
@KyleDelaneyは確かに、匿名のは、あなたがそこに1でも例のポイントとして確かに、匿名でないラムダ関数があり、ラムダなるために必要な条件ではありません
カーマインTambascia

@AliAnkaraliまたはrubys lambdaの使用に慣れる;)
Jimmy MG Lim

107

ラムダはインラインで定義される関数の一種です。ラムダに加えて、通常、関数やラムダなどへの参照を保持できる、ある種の変数タイプもあります。

たとえば、ラムダを使用しないC#コードは次のとおりです。

public Int32 Add(Int32 a, Int32 b)
{
    return a + b;
}

public Int32 Sub(Int32 a, Int32 b)
{
    return a - b;
}

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, Add);
    Calculator(10, 23, Sub);
}

これはCalculatorを呼び出し、2つの数値だけでなく、計算結果を取得するためにCalculator内で呼び出すメソッドを渡します。

C#2.0では、無名メソッドを取得しました。これにより、上記のコードが次のように短縮されます。

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a + b;
    });
    Calculator(10, 23, delegate(Int32 a, Int32 b)
    {
        return a - b;
    });
}

そして、C#3.0ではラムダを取得して、コードをさらに短くしました。

public delegate Int32 Op(Int32 a, Int32 b);

public void Calculator(Int32 a, Int32 b, Op op)
{
    Console.WriteLine("Calculator: op(" + a + ", " + b + ") = " + op(a, b));
}

public void Test()
{
    Calculator(10, 23, (a, b) => a + b);
    Calculator(10, 23, (a, b) => a - b);
}

デリゲートを明示的に定義する代わりに、Op単に使用してもよいFunc<int, int>
Mateen Ulhaq

Console.WriteLine("Calculator: op " + op.Method.Name + " (" + a + ", " + b + ") = " + op(a, b));最初の例をお勧めします。
Marc.2377 2017

受け入れられた答えが多くの異なる言語の人々を助けることであるのと同じくらい驚くべきですが、この答えはラムダの利点を理解するのに役立ち、それらが標準関数とどのように異なるかを理解するのに役立ちます。ここで、同様の結果を得るための以前の方法に対するラムダの利点を明確に見ることができます。
RTHarston

あなたの例をありがとう、より複雑な関数を持つことは、あまり最適化されていないように見える単純な関数よりもラムダの利点を理解するのに役立ちます
Sarah

71

「ラムダ」という名前は単なる歴史的な遺物です。私たちが話しているのは、その値が関数である式です。

簡単な例(次の行にScalaを使用)は次のとおりです。

args.foreach(arg => println(arg))

ここで、メソッドへの引数foreachは無名関数の式です。上記の行は多かれ少なかれこのようなものを書くことと同じです(かなり実際のコードではありませんが、あなたはアイデアを得るでしょう):

void printThat(Object that) {
  println(that)
}
...
args.foreach(printThat)

あなたが気にする必要がないことを除いて:

  1. 関数を別の場所で宣言します(後でコードを再訪するときに関数を探す必要があります)。
  2. 一度だけ使用するものに名前を付ける。

関数値に慣れると、次のようにすべての式に名前を付ける必要があるのと同じくらい、値なしで実行する必要があります。

int tempVar = 2 * a + b
...
println(tempVar)

必要な場所に式を記述するだけではなく、

println(2 * a + b)

正確な表記は言語によって異なります。ギリシャ語は必ずしも必要ではありません!;-)


62

これは、ラムダ計算を参照します。これは、ラムダ式のみを持つ正式なシステムであり、唯一の引数として関数を取り、関数を返す関数を表します。ラムダ計算のすべての関数はその型、すなわちですλ : λ → λ

Lispはラムダの概念を使用して、その無名関数リテラルに名前を付けました。このラムダは、xとyの2つの引数を取り、その積を返す関数を表します。

(lambda (x y) (* x y)) 

次のようにインラインで適用できます(50と評価されます):

((lambda (x y) (* x y)) 5 10)

あなたの使用λ : λ -> λは混乱していると思います(実際には無効です)。
アインポクルム

51

ラムダ計算は、置換の一貫した数学的理論です。学校の数学では、たとえばx+y=5とペアになりx−y=1ます。個々の方程式を操作する方法に加えて、方程式間の置換が論理的に行われていれば、これら2つの情報をまとめることもできます。ラムダ計算は、これらの置換を行う正しい方法を体系化しています。

y = x−1これが2番目の方程式の有効な再配置であることを考えると、これλ y = x−1は、シンボルの代わりにシンボルx−1を使用する関数を意味しますyλ y最初の方程式の各項に適用することを想像してください。用語がyその後である場合、置換を実行します。それ以外の場合は何もしません。これを紙の上でλ y行うと、それを適用すると最初の方程式が解けるようになります。

これは、コンピュータサイエンスやプログラミングなしの答えです。

私が考えることができる最も単純なプログラミングの例は、http//en.wikipedia.org/wiki/Joy_( programming_language)#How_it_worksからのものです

以下は、命令型プログラミング言語(C)で二乗関数を定義する方法です。

int square(int x)
{
    return x * x;
}

変数xは、関数が呼び出されたときに平方される実際の値に置き換えられる仮パラメーターです。関数型言語(Scheme)では、同じ関数が定義されます。

(define square
  (lambda (x) 
    (* x x)))

これは多くの点で異なりますが、仮パラメーターxを同じように使用します。


追加: http : //imgur.com/a/XBHub

ラムダ


14

少し単純化しすぎています:ラムダ関数は他の関数に渡して渡すことができ、そのロジックにアクセスできます。

C#では、ラムダ構文は匿名デリゲートと同じ方法で単純なメソッドにコンパイルされることがよくありますが、分解してそのロジックを読み取ることもできます。

たとえば(C#3の場合):

LinqToSqlContext.Where( 
    row => row.FieldName > 15 );

LinqToSqlはその関数(x> 15)を読み取り、実際のSQLに変換して式ツリーを使用して実行できます。

上記のステートメントは次のようになります。

select ... from [tablename] 
where [FieldName] > 15      --this line was 'read' from the lambda function

これは、通常のメソッドや匿名デリゲート(実際には単なるコンパイラマジック)とは異なり、読み取ることができません。

C#でラムダ構文を使用するすべてのメソッドを式ツリー(つまり、実際のラムダ関数)にコンパイルできるわけではありません。例えば:

LinqToSqlContext.Where( 
    row => SomeComplexCheck( row.FieldName ) );

現在、式ツリーを読み取ることができません-SomeComplexCheckを分解できません。SQLステートメントはwhereなしで実行され、データのすべての行が通過しSomeComplexCheckます。

ラムダ関数を無名メソッドと混同しないでください。例えば:

LinqToSqlContext.Where( 
    delegate ( DataRow row ) { 
        return row.FieldName > 15; 
    } );

これには「インライン」関数もありますが、今回は単なるコンパイラの魔法です-C#コンパイラはこれを自動生成された名前を持つ新しいインスタンスメソッドに分割します。

匿名メソッドは読み取ることができないため、ラムダ関数の場合のようにロジックを変換することはできません。


7

この記事のLambdasの説明が気に入っています:LINQの進化とC#の設計への影響。ラムダスの実際の世界を示し、それを実用的な例として構築しているので、それは私には非常に理にかなっています。

彼らの簡単な説明:ラムダは、コード(関数)をデータとして扱う方法です。


7

Rubyのラムダの例は次のとおりです。

hello = lambda do
    puts('Hello')
    puts('I am inside a proc')
end

hello.call

次の出力を生成します:

Hello
I am inside a proc

5

@Brian C#、LINQおよび非LINQ演算子では常にラムダを使用します。例:

string[] GetCustomerNames(IEnumerable<Customer> customers)
 { return customers.Select(c=>c.Name);
 }

C#の前は、AJAX関数へのコールバックにJavaScriptで匿名関数を使用していました。

getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});

ただし、C#のラムダ構文の興味深い点は、それ自体では型を推測できない(つまり、var foo =(x、y)=> x * yと入力できない)が、その型によっては割り当てられると、それらは式を表すデリゲートまたは抽象構文ツリーとしてコンパイルされます(これは、LINQオブジェクトマッパーが「言語統合」マジックを行う方法です)。

LISPのラムダは、引用演算子に渡され、リストのリストとしてトラバースすることもできます。一部の強力なマクロはこの方法で作成されます。


5

質問は正式に大いに回答されているので、これについてはこれ以上付け加えません。

数学やプログラミングについてほとんど、またはまったく知らない人に非常にシンプルで非公式の言葉で、私はそれを、いくつかの入力を受け取り、いくつかの作業を行い、いくつかの出力を生成し、特定の名前を持たない小さな「マシン」または「ボックス」として説明します、しかし私たちはそれがどこにあるかを知っており、この知識だけでそれを使用します。

実際的に言えば、関数が何であるかを知っている人にとって、それは名前のない関数であり、通常はメモリを参照するだけで使用できるメモリ内のポイントに置かれる関数であることを伝えます(通常、変数-彼らが関数ポインターの概念について聞いた場合、私はそれらを同様の概念として使用します)-この回答はかなりの基本(クロージャーなどの言及なし)をカバーしますが、簡単にポイントを得ることができます。



4

ここにC ++ 11の例が表示されないからといって、ここからこの素晴らしい例を投稿します。検索後、私が見つけた最も明確な言語固有の例です。

こんにちは、ラムダス、バージョン1

template<typename F>

void Eval( const F& f ) {
        f();
}
void foo() {
        Eval( []{ printf("Hello, Lambdas\n"); } );
}

こんにちは、ラムダス、バージョン2:

void bar() {
    auto f = []{ printf("Hello, Lambdas\n"); };
    f();
}

3

マクロ置換と、ExecScript {}およびEvaluate()関数を使用しているVisual FoxProで作業しているため、ラムダ式に頭を抱え込むのに問題があります。

? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");

FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)

正式なラムダを使用することの明確な利点の1つは、(私は)コンパイル時のチェックです。Foxは、実行しようとするまで、上記のテキスト文字列を入力したかどうかを認識しません。

これは、データ駆動型コードにも役立ちます。ルーチン全体をデータベースのメモフィールドに格納して、実行時に評価するだけです。これにより、実際にソースにアクセスすることなく、アプリケーションの一部を調整できます。(しかし、それは完全に別のトピックです。)


3

comp-sciの背景がない人にとって、コンピュータサイエンスの世界でのラムダとは何ですか?

シンプルで読みやすいpythonコードを使用して、一歩一歩直感的に説明します。

つまり、ラムダは匿名のインライン関数です。

lambdas基本的な計算の背景を持つ新入生として理解するために、割り当てから始めましょう。

割り当ての青写真は「名前=値」です。以下を参照してください。

In [1]: x = 1
   ...: y = 'value'
In [2]: x
Out[2]: 1
In [3]: y
Out[3]: 'value'

「x」、「y」は名前、1、「値」は値です。数学の関数を試す

In [4]: m = n**2 + 2*n + 1
NameError: name 'n' is not defined

エラーレポート。
コードとして数学を直接記述することはできません。「n」を定義するか、値に割り当てる必要があります。

In [8]: n = 3.14
In [9]: m = n**2 + 2*n + 1
In [10]: m
Out[10]: 17.1396

2つの分離したラインを1つに結合することを主張するとどうなるでしょうか。来るlambda

In [13]: j = lambda i: i**2 + 2*i + 1
In [14]: j
Out[14]: <function __main__.<lambda>>

エラーは報告されていません。

これは一目でlambdaわかります。数学で行うように、関数を1行で直接コンピューターに書き込むことができます。

後で見ます。

「割り当て」についてさらに掘り下げていきましょう。

上に示したように、等号=は単純なデータ(1および '値')タイプと単純な式(n ** 2 + 2 * n + 1)で機能します。

これを試して:

In [15]: x = print('This is a x')
This is a x
In [16]: x
In [17]: x = input('Enter a x: ')
Enter a x: x

シンプルなステートメントで機能します。Python7には11種類あります。シンプルなステートメント— Python 3.6.3ドキュメント

複合ステートメントはどうですか

In [18]: m = n**2 + 2*n + 1 if n > 0
SyntaxError: invalid syntax
#or
In [19]: m = n**2 + 2*n + 1, if n > 0
SyntaxError: invalid syntax

それが機能するdefことを可能にします

In [23]: def m(n):
    ...:     if n > 0:
    ...:         return n**2 + 2*n + 1
    ...:
In [24]: m(2)
Out[24]: 9

多田、それを分析して、「m」は名前、「n ** 2 + 2 * n + 1」は値です。:「=」の変形です。
理解するためだけに、すべてが割り当てから始まり、すべてが割り当てであることを確認してください。

ここでに戻ります。lambda「m」という名前の関数があります。

試してください:

In [28]: m = m(3)
In [29]: m
Out[29]: 16

ここには「m」の名前が2つあります。関数にはmすでに重複した名前があります。

それは次のようなフォーマットです:

In [27]: m = def m(n):
    ...:         if n > 0:
    ...:             return n**2 + 2*n + 1
    SyntaxError: invalid syntax

それは賢い戦略ではないので、エラーレポート

それらの1つを削除し、名前なしで関数を設定する必要があります。

m = lambda n:n**2 + 2*n + 1

「無名関数」と呼ばれています

結論として、

  1. lambda 数学の場合と同様に、関数を1つの直線で記述できるインライン関数内
  2. lambda 匿名です

お役に立てれば。


2

名前のない機能です。例えばc#ではあなたが使うことができます

numberCollection.GetMatchingItems<int>(number => number > 5);

5より大きい数値を返します。

number => number > 5

ここはラムダ部分です これは、パラメーター(数値)を取り、ブール値(数値> 5)を返す関数を表します。GetMatchingItemsメソッドは、コレクション内のすべてのアイテムでこのラムダを使用し、一致するアイテムを返します。


2

JavaScriptでは、例えば、機能は他のすべてと同じ混合型として扱われ(intstringfloatbool)。そのため、関数をその場で作成し、それらを物事に割り当て、後で呼び出すことができます。それは便利ですが、使いすぎたくないものではありません。そうしないと、コードを保守しなければならない人を混乱させてしまいます...

これは、このうさぎの穴がどの程度深くなるかを確認するために私が遊んでいたコードです。

var x = new Object;
x.thingy = new Array();
x.thingy[0] = function(){ return function(){ return function(){ alert('index 0 pressed'); }; }; }
x.thingy[1] = function(){ return function(){ return function(){ alert('index 1 pressed'); }; }; }
x.thingy[2] = function(){ return function(){ return function(){ alert('index 2 pressed'); }; }; }

for(var i=0 ;i<3; i++)
    x.thingy[i]()()();

2

CSのコンテキストでは、ラムダ関数は、数式の記号評価の問題に取り組む抽象的な数学概念です。そのコンテキストでは、ラムダ関数はラムダ項と同じです。

しかし、プログラミング言語では、それは何か違うものです。これは「インプレース」で宣言されたコードの一部であり、「ファーストクラスの市民」として渡すことができます。この概念は有用であるように思われたので、ほとんどすべての人気のある現代のプログラミング言語に取り入れられましたラムダ関数のこれまでの投稿を参照)。


2

A Lambda Function、またはSmall Anonymous Function、周囲に合格し、あなたのコード内で使用可能な機能の自己完結型ブロックです。-ラムダは異なるプログラミング言語で異なる名前があるLambdaはPythonKotlinClosureスウィフト、またはBlockCObjective-Cのを。ラムダの意味はこれらの言語で非常に似ていますが、時々若干の違いがあります。

Lambda(Closure)がSwift 4.2でSorted()メソッドを使用してどのように機能するかを見てみましょう–通常の関数から最も短い式まで:

let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"]

1.通常の機能

func backward(_ n1: String, _ n2: String) -> Bool {
    return n1 > n2
}
var reverseOrder = coffee.sorted(by: backward)


// RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"]

2.クロージャ式

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in
    return n1 > n2
})

3.インラインクロージャ式

reverseOrder = coffee.sorted(by: { (n1: String, n2: String) -> Bool in return n1 > n2 } )

4.コンテキストから型を推論

reverseOrder = coffee.sorted(by: { n1, n2 in return n1 > n2 } )

5.単一式のクロージャーからの暗黙的な戻り値

reverseOrder = coffee.sorted(by: { n1, n2 in n1 > n2 } )

6.省略引数名

reverseOrder = coffee.sorted(by: { $0 > $1 } )

// $0 and $1 are closure’s first and second String arguments.

7.オペレーターメソッド

reverseOrder = coffee.sorted(by: >)

// RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"]

お役に立てれば。


1

私も手に入れました。私はこれをJSで試しました:

var addAndMult = function(x) {
        return (function(y) {
            return (function(z) {
                return (x+y)*z; 
                });
            });
        };

2から4を追加し、結果を6で乗算します。ただし、読みにくい場合があります:(

また、興味深いforEach関数を作成しました。

var forEach = function(arr) {
            return (function(x) {
            for (var i=0; arr[i]; i++) {
                 x(arr[i]);
             }
        });
    }

forEach([1,2,3,4,5])(console.log);

このメソッドは、配列を反復してアクションを実行します-コンソールに出力する場合。今、私もlabmdasが強力な理由を理解しています。


1

コンピュータプログラミングでは、ラムダは外部ソースからいくつかの引数をとるコード(ステートメント、式、またはそれらのグループ)です。それは常に無名関数である必要はありません-それらを実装する多くの方法があります。

数学者にはない、式、ステートメント、関数の明確な分離があります。

プログラミングにおける「関数」という言葉も異なります-「関数は実行する一連のステップ」です(ラテン語の「実行」から)。数学では、それは変数間の相関についての何かです。

関数型言語は可能な限り数式に類似するように努めており、それらの単語はほとんど同じ意味です。しかし、他のプログラミング言語ではそれが異なります。


0

質問は完全に回答されています。詳細については説明しません。錆びて数値計算を書くときの使い方を共有したい。

ラムダ(無名関数)の例があります

let f = |x: f32| -> f32 { x * x - 2.0 };
let df = |x: f32| -> f32 { 2.0 * x };

ニュートン・ラフソン法のモジュールを書いているとき、それは一次および二次導関数として使用されていました。(ニュートンラフソン法とは何か知りたい場合は、「https://en.wikipedia.org/wiki/Newton%27s_method」にアクセスしてください

次のような出力

println!("f={:.6}      df={:.6}", f(10.0), df(10.0))

f=98.000000       df=20.000000

0

配達オプション付きのレストランがあり、30分以内に完了する必要のある注文があるとします。重要なのは、食事を暖かく縛りつけておく限り、車や裸足で自転車で食べ物を送っても、クライアントは通常気にしません。それでは、このイディオムを匿名で定義されたトランスポート関数を使用してJavascriptに変換してみましょう。

以下では、デリバリーの方法を定義します。つまり、関数に名前を定義します。

// ES5 
var food = function withBike(kebap, coke) {
return (kebap + coke); 
};

この転送を達成するために矢印/ラムダ関数を使用するとどうなるでしょうか。

// ES6    
const food = (kebap, coke) => { return kebap + coke };

クライアントに違いはなく、食べ物を送る方法を考えるのに時間を費やすこともありません。送信してください。

ところで、コーラ付きのケバップはお勧めしません。これが、上位コードがエラーを出す理由です。楽しんで。

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