ラムダ(関数)とは何ですか?
-
08-06-2019 - |
質問
コンプサイエンスのバックグラウンドのない人にとって、コンピューター サイエンスの世界におけるラムダとは何でしょうか?
解決
ラムダの由来は、 ラムダ微積分 プログラミングにおける無名関数を指します。
なぜこれがクールなのでしょうか?これにより、名前を付けずに簡単に捨て関数を作成できます。また、クロージャを記述する優れた方法も提供します。その力を使えばこんな事も出来るのです。
パイソン
def adder(x):
return lambda y: x + y
add5 = adder(5)
add5(1)
6
Python のスニペットからわかるように、関数加算器は引数 x を受け取り、別の引数 y を取る匿名関数 (ラムダ) を返します。この匿名関数を使用すると、関数から関数を作成できます。これは単純な例ですが、ラムダとクロージャの持つ力が伝わるはずです。
他の言語での例
パール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
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 = function () use (&$a, &$b) {
echo $a + $b;
};
echo $lambda();
ハスケル
(\x y -> x + y)
ジャワ 見る この郵便受け
// 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
他のヒント
ラムダは関数の一種であり、インラインで定義されます。通常、ラムダに加えて、ラムダなどの関数への参照を保持できるある種の変数タイプもあります。
たとえば、ラムダを使用しない 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);
}
それは、 ラムダ計算, これは、唯一の引数として関数を受け取り、関数を返す関数を表すラムダ式だけを備えた形式的なシステムです。ラムダ計算の関数はすべてそのタイプです。つまり、 λ : λ → λ
.
Lisp はラムダの概念を使用して、その無名関数リテラルに名前を付けました。このラムダは、2 つの引数 x と y を受け取り、その積を返す関数を表します。
(lambda (x y) (* x y))
このようにインラインで適用できます (次のように評価されます) 50):
((lambda (x y) (* x y)) 5 10)
「ラムダ」という名前は歴史上の産物にすぎません。ここで話しているのは、値が関数である式についてだけです。
簡単な例 (次の行で Scala を使用) は次のとおりです。
args.foreach(arg => println(arg))
ここで、に対する引数は、 foreach
Method は無名関数の式です。上記の行は、次のようなコードを記述するのとほぼ同じです (実際のコードではありませんが、アイデアは理解できるでしょう)。
void printThat(Object that) {
println(that)
}
...
args.foreach(printThat)
ただし、次のことを気にする必要はありません。
- 関数を別の場所で宣言します (後でコードを再参照するときに、関数を探す必要があります)。
- 一度しか使わないものに名前を付ける。
関数値に慣れてしまうと、値を使わずに処理するのは、次のようなすべての式に名前を付ける必要があるのと同じくらい愚かに思えます。
int tempVar = 2 * a + b
...
println(tempVar)
必要な場所に式を記述するだけではなく、次のようにします。
println(2 * a + b)
正確な表記は言語によって異なります。ギリシャ語は必ずしも必要なわけではありません。;-)
ラムダ計算は一貫した数学的代入理論です。たとえば学校の数学で見られるのは、 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_(プログラミング言語)#How_it_works:
正方形の関数が命令的なプログラミング言語(c)でどのように定義されるかは次のとおりです。
int square(int x) { return x * x; }
変数xは、関数が呼び出されたときに二乗する実際の値に置き換える正式なパラメーターです。機能的言語(スキーム)では、同じ関数が定義されます。
(define square (lambda (x) (* x x)))
これは多くの点で異なりますが、同じ方法で正式なパラメーターxを使用しています。
追加した: http://imgur.com/a/XBHub
少し単純化しすぎています:ラムダ関数は、他の関数に渡してそのロジックにアクセスできる関数です。
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
.
Lambda 関数を匿名メソッドと混同しないでください。例えば:
LinqToSqlContext.Where(
delegate ( DataRow row ) {
return row.FieldName > 15;
} );
これには「インライン」関数もありますが、今回は単なるコンパイラの魔法です。C# コンパイラはこれを、自動生成された名前を持つ新しいインスタンス メソッドに分割します。
匿名メソッドは読み取ることができないため、ラムダ関数のようにロジックを変換することはできません。
この記事の Lambda の説明が気に入っています。 LINQ の進化と C# の設計への影響. 。これは、Lambda の現実の世界を示し、実用的な例として構築しているため、私にとって非常に理にかなっていました。
彼らの簡単な説明は次のとおりです。ラムダは、コード (関数) をデータとして扱う方法です。
Ruby のラムダの例は次のとおりです。
hello = lambda do
puts('Hello')
puts('I am inside a proc')
end
hello.call
次の出力が生成されます。
Hello
I am inside a proc
@Brian私はC#、LINQおよび非LINQ演算子で常にラムダを使用しています。例:
string[] GetCustomerNames(IEnumerable<Customer> customers)
{ return customers.Select(c=>c.Name);
}
C# が登場する前は、Ajax という用語が生まれる前に、JavaScript で AJAX 関数へのコールバックに匿名関数を使用していました。
getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});
ただし、C# のラムダ構文の興味深い点は、それ自体では型を推論できない (つまり、var foo = (x,y) => x * y と入力できない) が、その型に応じて異なることです。に割り当てられると、式を表すデリゲートまたは抽象構文ツリーとしてコンパイルされます (これが、LINQ オブジェクト マッパーが「言語に統合された」魔法を実行する方法です)。
LISP のラムダは引用演算子に渡して、リストのリストとして走査することもできます。いくつかの強力なマクロはこの方法で作成されます。
この質問には正式に十分な回答が得られているので、これ以上付け加えることはしません。
非常に簡単に言うと、 非公式 数学やプログラミングについてほとんど、またはまったく知らない人に言葉を与えると、私はそれを、何らかの入力を受け取り、何らかの作業を行い、何らかの出力を生成する小さな「マシン」または「ボックス」として説明します。特定の名前はありませんが、どこにあるかはわかっています。それは事実であり、この知識だけで私たちはそれを使用します。
実際的に言えば、関数が何であるかを知っている人には、それは名前のない関数であり、通常はメモリ内のポイントに配置され、そのメモリを参照するだけで使用できます (通常は、変数 - 彼らが関数ポインタの概念について聞いたことがある場合、私はそれらを同様の概念として使用します) - この回答はかなり基本的なものをカバーしています(クロージャなどについては言及していません)が、ポイントは簡単に理解できます。
これは匿名関数と考えることができます。さらに詳しい情報は次のとおりです。 ウィキペディア - 匿名関数
ここでは 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();
}
Visual FoxPro で作業しているため、ラムダ式について理解するのに苦労しています。Visual FoxPro にはマクロ置換機能と、ほぼ同じ目的を果たすと思われる ExecScript{} 関数と Evaluate() 関数があります。
? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");
FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)
正式なラムダを使用することの明確な利点の 1 つは、(私が推測する) コンパイル時のチェックです。Fox は、上記のテキスト文字列をタイプミスしても、それを実行しようとするまでわかりません。
これはデータ駆動型コードにも役立ちます。ルーチン全体をデータベースのメモ フィールドに保存し、実行時に評価するだけです。これにより、実際にソースにアクセスしなくても、アプリケーションの一部を調整できます。(しかし、それは全く別の話です。)
コンプサイエンスのバックグラウンドのない人にとって、コンピューター サイエンスの世界におけるラムダとは何でしょうか?
シンプルで読みやすい Python コードで、直感的にステップバイステップで説明します。
つまり、ラムダは単なる匿名のインライン関数です。
理解するために課題から始めましょう lambdas
基本的な算術の背景を持つ新入生として。
割り当ての青写真は「名前 = 値」です。以下を参照してください。
In [1]: x = 1
...: y = 'value'
In [2]: x
Out[2]: 1
In [3]: y
Out[3]: 'value'
「x」、「y」は名前、1、「value」は値です。数学で関数を試してみる
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
単純なステートメントで機能します。Python には 11 種類のステートメントがあります 7。単純なステートメント — 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
それは「匿名関数」と呼ばれます
結論は、
lambda
インライン関数では、数学のように関数を 1 つの直線で書くことができます。lambda
匿名です
お役に立てれば。
名前のない関数です。たとえば、C#では使用できます
numberCollection.GetMatchingItems<int>(number => number > 5);
5 より大きい数値を返します。
number => number > 5
ここのラムダ部分です。これは、パラメータ (数値) を受け取り、ブール値 (数値 > 5) を返す関数を表します。GetMatchingItems メソッドは、コレクション内のすべての項目に対してこのラムダを使用し、一致する項目を返します。
たとえば、JavaScript では、関数は他のすべてと同じ混合型として扱われます (int
, string
, float
, bool
)。そのため、その場で関数を作成し、それらを物事に割り当て、後で呼び出すことができます。これは便利ですが、あまり使いすぎたくないものです。そうしないと、後でコードを保守する必要がある人全員が混乱することになります...
これは、このウサギの穴がどれくらい深くなるかを確認するために私が試していたコードです。
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]()()();
私もわかりました。これを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);
このメソッドは配列を反復し、アクションを実行します (この場合、コンソールに出力します)。私もラボダが強力な理由を理解しました。
コンピューター プログラミングにおいて、ラムダは外部ソースからいくつかの引数を受け取るコード (ステートメント、式、またはそれらのグループ) です。常に匿名関数である必要はありません。実装する方法はたくさんあります。
私たちは式、ステートメント、関数を明確に区別していますが、数学者にはありません。
プログラミングにおける「関数」という言葉も異なります。「関数は実行する一連のステップです」(ラテン語の「実行」に由来)という言葉があります。数学では、変数間の相関関係に関するものです。
関数型言語は可能な限り数式に似せようとしており、単語の意味はほぼ同じです。しかし、他のプログラミング言語では異なります。
あ
Lambda Function
, 、またはSmall Anonymous Function
, は、コード内で渡して使用できる自己完結型の機能ブロックです。Lambda はプログラミング言語ごとに異なる名前を持っています。Lambda
で パイソン そして コトリン,Closure
で 迅速, 、 またはBlock
で C そして 目的-C. 。ラムダの意味はこれらの言語で非常に似ていますが、若干の違いがある場合があります。
Swift 4.2 で、sorted() メソッドを使用して Lambda (Closure) がどのように動作するかを、通常の関数から最短の式まで見てみましょう。
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"]
お役に立てれば。
質問には十分に答えられていますので、詳細には触れたくありません。Rustで数値計算を書く際の使い方を共有したいと思います。
ラムダ(無名関数)の例あり
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
配達オプションを備えたレストランがあり、30 分以内に完了する必要がある注文があると想像してください。重要なのは、食事を温かく保ち、縛られていれば、顧客は通常、車で自転車で食べ物を送っても、裸足で送っても気にしないということです。そこで、匿名で定義されたトランスポート関数を使用して、このイディオムを Javascript に変換してみましょう。
以下では、配信方法を定義します。つまり、関数に名前を定義します。
// ES5
var food = function withBike(kebap, coke) {
return (kebap + coke);
};
この転送を達成するためにアロー/ラムダ関数を使用するとどうなるでしょうか。
// ES6
const food = (kebap, coke) => { return kebap + coke };
クライアントにとっては何の違いもなく、食べ物を送る方法を考えるのに時間を無駄にする必要はないことがわかります。ただ送ってください。
ところで、コーラ入りケバップはお勧めしません。これが、上のコードでエラーが発生する理由です。楽しむ。