コンプ科学のバックグラウンドがない人にとって、コンピューター サイエンスの世界でラムダとは何ですか?
23 に答える
ラムダはラムダ計算に由来し、プログラミングにおける無名関数を指します。
なぜこれがクールなのですか?これにより、名前を付けずに簡単な破棄関数を作成できます。また、クロージャを記述するための優れた方法も提供します。その力があれば、こんなことができる。
パイソン
def adder(x):
return lambda y: x + y
add5 = adder(5)
add5(1)
6
Python のスニペットからわかるように、関数 adder は引数 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 = 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
ラムダは、インラインで定義される関数の一種です。ラムダに加えて、通常、ラムダなどの関数への参照を保持できるある種の変数タイプもあります。
たとえば、ラムダを使用しない 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);
}
「ラムダ」という名前は単なる歴史的な人工物です。私たちが話しているのは、値が関数である式だけです。
簡単な例 (次の行に Scala を使用) は次のとおりです。
args.foreach(arg => println(arg))
メソッドの引数foreach
は無名関数の式です。上記の行は、次のようなものを書くこととほぼ同じです (実際のコードではありませんが、アイデアは得られます)。
void printThat(Object that) {
println(that)
}
...
args.foreach(printThat)
ただし、次のことを気にする必要はありません。
- 関数を別の場所で宣言します (後でコードを再確認するときにそれを探す必要があります)。
- 一度しか使わないものに名前を付ける。
値を関数化することに慣れると、値を使わずにやらなければならないのは、次のようなすべての式に名前を付ける必要があるのと同じくらいばかげているように思えます。
int tempVar = 2 * a + b
...
println(tempVar)
必要な場所に式を書くだけではなく:
println(2 * a + b)
正確な表記は言語によって異なります。ギリシャ語は必ずしも必要ではありません。;-)
これはラムダ計算を指します。これはラムダ式のみを持つ正式なシステムであり、その唯一の引数として関数を取り、関数を返す関数を表します。ラムダ計算のすべての関数は、その型、つまり ですλ : λ → λ
。
Lisp はラムダの概念を使用して無名関数リテラルに名前を付けました。このラムダは、x と y の 2 つの引数を取り、それらの積を返す関数を表します。
(lambda (x y) (* x y))
次のようにインラインで適用できます ( 50に評価されます)。
((lambda (x y) (* x y)) 5 10)
ラムダ計算は、置換に関する一貫した数学的理論です。学校の数学では、たとえば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 を同じように使用しています。
少し単純化しすぎました: ラムダ関数は、他の関数に丸く渡すことができる関数であり、アクセスされるロジックです。
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
これは、 readできないため、通常のメソッドや匿名デリゲート (実際にはコンパイラの魔法にすぎません) とは異なります。
ラムダ構文を使用する C# のすべてのメソッドを式ツリー (つまり、実際のラムダ関数) にコンパイルできるわけではありません。例えば:
LinqToSqlContext.Where(
row => SomeComplexCheck( row.FieldName ) );
現在、式ツリーを読み取ることができません - SomeComplexCheck を分解することはできません。SQL ステートメントは where なしで実行され、データ内のすべての行が通過しSomeComplexCheck
ます。
Lambda 関数を匿名メソッドと混同しないでください。例えば:
LinqToSqlContext.Where(
delegate ( DataRow row ) {
return row.FieldName > 15;
} );
これには「インライン」関数もありますが、今回は単なるコンパイラ マジックです。C# コンパイラはこれを、自動生成された名前を持つ新しいインスタンス メソッドに分割します。
匿名メソッドは読み取ることができないため、ラムダ関数の場合のようにロジックを変換することはできません。
この記事のラムダの説明が気に入っています: The Evolution Of LINQ And Its Impact On The Design Of C# . ラムダの実際の世界を示しており、実際の例としてそれを構築しているので、私にとっては非常に理にかなっています。
彼らの簡単な説明: ラムダは、コード (関数) をデータとして扱う方法です。
@Brian私は、C#、LINQおよび非LINQ演算子で常にラムダを使用しています。例:
string[] GetCustomerNames(IEnumerable<Customer> customers)
{ return customers.Select(c=>c.Name);
}
C# の前は、Ajax という用語が造られる前に、AJAX 関数へのコールバックに JavaScript で匿名関数を使用していました。
getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});
ただし、C# のラムダ構文の興味深い点は、それ自体では型を推測できないことです (つまり、var foo = (x,y) => x * y と入力することはできません)。に割り当てられると、式を表すデリゲートまたは抽象構文ツリーとしてコンパイルされます (これは、LINQ オブジェクト マッパーが "言語統合" マジックを実行する方法です)。
LISP のラムダは、引用演算子に渡して、リストのリストとしてトラバースすることもできます。いくつかの強力なマクロは、この方法で作成されます。
Ruby でのラムダの例は次のとおりです。
hello = lambda do
puts('Hello')
puts('I am inside a proc')
end
hello.call
次の出力が生成されます。
Hello
I am inside a proc
ここで 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();
}
マクロ置換とExecScript{}およびEvaluate()関数を備えたVisual FoxProで作業しているため、ラムダ式に頭を悩ませるのに苦労しています。これらはほとんど同じ目的を果たしているようです。
? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");
FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)
正式なラムダを使用することの明確な利点の1つは、コンパイル時のチェックです。Foxは、実行を試みるまで、上記のテキスト文字列を入力したかどうかを知りません。
これは、データ駆動型コードにも役立ちます。ルーチン全体をデータベースのメモフィールドに格納し、実行時に評価することができます。これにより、実際にソースにアクセスしなくても、アプリケーションの一部を微調整できます。(しかし、それはまったく別のトピックです。)
あなたはそれを匿名関数と考えることができます - ここにいくつかの詳細情報があります:ウィキペディア - 匿名関数
名前のない機能です。たとえば、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]()()();