对于没有计算机科学背景的人来说,计算机科学领域的 lambda 是什么?
23 回答
Lambda 来自于Lambda Calculus,指的是编程中的匿名函数。
为什么这很酷?它允许您编写快速丢弃函数而不用命名它们。它还提供了一种编写闭包的好方法。有了这种力量,你可以做这样的事情。
Python
def adder(x):
return lambda y: x + y
add5 = adder(5)
add5(1)
6
从 Python 的代码片段可以看出,函数加法器接受一个参数 x,并返回一个匿名函数或 lambda,它接受另一个参数 y。该匿名函数允许您从函数创建函数。这是一个简单的例子,但它应该传达出 lambda 和闭包的强大功能。
其他语言的例子
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
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 稍有不同,因为您不能使用与调用函数完全相同的语法来调用 lambda,但它仍然具有 lambda。
def adder(x)
lambda { |y| x + y }
end
add5 = adder(5)
add5[1] == 6
Ruby 是 Ruby,有一个 lambdas 的简写,所以你可以这样定义adder
:
def adder(x)
-> y { x + y }
end
R
adder <- function(x) {
function(y) x + y
}
add5 <- adder(5)
add5(1)
#> [1] 6
lambda 是一种内联定义的函数。除了 lambda,您通常还有某种变量类型可以保存对函数、lambda 或其他函数的引用。
例如,这是一段不使用 lambda 的 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,传递的不仅仅是两个数字,而是在 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 中,我们得到了 lambdas,这使得代码更短:
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);
}
“lambda”这个名字只是一个历史产物。我们谈论的只是一个表达式,其值是一个函数。
一个简单的例子(下一行使用 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)
确切的符号因语言而异;并不总是需要希腊语!;-)
它指的是lambda 演算,这是一个只有 lambda 表达式的形式系统,它表示一个函数,该函数接受一个函数作为其唯一参数并返回一个函数。lambda 演算中的所有函数都属于该类型,即λ : λ → λ
.
Lisp 使用 lambda 概念来命名它的匿名函数字面量。这个 lambda 表示一个函数,它接受两个参数 x 和 y,并返回它们的乘积:
(lambda (x y) (* x y))
它可以像这样在线应用(评估为50):
((lambda (x y) (* x y)) 5 10)
λ演算是一种一致的数学替代理论。例如,在学校数学中可以看到x+y=5
与x−y=1
. 除了处理单个方程的方法外,还可以将来自这两个方程的信息放在一起,前提是交叉方程替换是在逻辑上完成的。Lambda 演算编纂了进行这些替换的正确方法。
鉴于这y = x−1
是对第二个等式的有效重新排列,这:λ y = x−1
表示将符号替换为符号的x−1
函数y
。现在想象一下应用于λ y
第一个等式中的每一项。如果一个词是y
然后执行替换;否则什么也不做。如果你在纸上做到这一点,你会看到应用它如何λ y
使第一个方程可解。
这是一个没有任何计算机科学或编程的答案。
我能想到的最简单的编程示例来自http://en.wikipedia.org/wiki/Joy_(programming_language)#How_it_works:
以下是在命令式编程语言 (C) 中如何定义 square 函数:
int square(int x) { return x * x; }
变量 x 是一个形式参数,它在调用函数时被实际平方值替换。在函数式语言(方案)中,将定义相同的函数:
(define square (lambda (x) (* x x)))
这在很多方面是不同的,但它仍然以相同的方式使用形式参数 x。
补充:http: //imgur.com/a/XBHub
有点过于简单了:lambda 函数是一个可以传递给其他函数并且可以访问它的逻辑的函数。
在 C# 中,lambda 语法通常以与匿名委托相同的方式编译为简单方法,但也可以分解并读取其逻辑。
例如(在 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# 中使用 lambda 语法的所有方法都可以编译为表达式树(即实际的 lambda 函数)。例如:
LinqToSqlContext.Where(
row => SomeComplexCheck( row.FieldName ) );
现在无法读取表达式树 - SomeComplexCheck 无法分解。SQL 语句将在没有 where 的情况下执行,并且数据中的每一行都将通过SomeComplexCheck
.
Lambda 函数不应与匿名方法混淆。例如:
LinqToSqlContext.Where(
delegate ( DataRow row ) {
return row.FieldName > 15;
} );
这也有一个“内联”函数,但这一次它只是编译器的魔法——C# 编译器会将其拆分为一个具有自动生成名称的新实例方法。
无法读取匿名方法,因此无法将逻辑转换为 lambda 函数。
这个问题已经得到了很好的正式回答,所以我不会尝试在此添加更多内容。
对于对数学或编程知之甚少或一无所知的人,用非常简单、非正式的话来说,我会将其解释为一个小型“机器”或“盒子”,它接受一些输入、进行一些工作并产生一些输出,没有特定的名称,但是我们知道它在哪里,并且仅凭这些知识,我们就可以使用它。
实际上,对于一个知道函数是什么的人,我会告诉他们它是一个没有名称的函数,通常放在内存中的某个点,只需通过引用该内存就可以使用(通常通过使用一个变量——如果他们听说过函数指针的概念,我会将它们用作类似的概念)——这个答案涵盖了非常基础的知识(没有提到闭包等),但人们可以很容易地理解这一点。
我喜欢这篇文章中对 Lambdas 的解释:LINQ 的演变及其对 C# 设计的影响。这对我来说很有意义,因为它为 Lambdas 展示了一个真实的世界,并将其构建为一个实际的例子。
他们的快速解释:Lambda 是一种将代码(函数)视为数据的方法。
Ruby 中的 lambda 示例如下:
hello = lambda do
puts('Hello')
puts('I am inside a proc')
end
hello.call
将生成以下输出:
Hello
I am inside a proc
@Brian 我一直在 C#、LINQ 和非 LINQ 运算符中使用 lambda。例子:
string[] GetCustomerNames(IEnumerable<Customer> customers)
{ return customers.Select(c=>c.Name);
}
在 C# 之前,我在 JavaScript 中使用匿名函数来回调 AJAX 函数,甚至在 Ajax 一词被创造出来之前:
getXmlFromServer(function(result) {/*success*/}, function(error){/*fail*/});
不过,C# 的 lambda 语法的有趣之处在于,它们本身无法推断出它们的类型(即,你不能键入 var foo = (x,y) => x * y),但取决于它们的类型分配给,它们将被编译为代表表达式的委托或抽象语法树(这就是 LINQ 对象映射器如何发挥其“语言集成”的魔力)。
LISP 中的 Lambda 也可以传递给引号运算符,然后作为列表的列表进行遍历。一些强大的宏就是这样制作的。
只是因为我在这里看不到 C++11 示例,我将继续从这里发布这个很好的示例。经过搜索,这是我能找到的最清晰的语言特定示例。
你好,Lambdas,版本 1
template<typename F>
void Eval( const F& f ) {
f();
}
void foo() {
Eval( []{ printf("Hello, Lambdas\n"); } );
}
你好,Lambdas,第 2 版:
void bar() {
auto f = []{ printf("Hello, Lambdas\n"); };
f();
}
A
Lambda Function
或 aSmall Anonymous Function
是一个独立的功能块,可以在您的代码中传递和使用。Lambda 在不同的编程语言中有不同的名称——Lambda
在Python和Kotlin中,Closure
在Swift中,或者Block
在C和Objective-C中。尽管 lambda 对这些语言的含义非常相似,但有时会略有不同。
让我们看看 Closure (Lambda) 在 Swift 中是如何工作的:
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"]
对于没有计算机科学背景的人来说,计算机科学领域的 lambda 是什么?
我将用简单易读的python代码一步一步直观地说明它。
简而言之,lambda 只是一个匿名的内联函数。
让我们从作业开始,了解lambdas
作为具有基本算术背景的大一新生。
赋值的蓝图是'name = value',见:
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
现在可以了,如果您坚持将两条单独的线合二为一怎么办。来了lambda
In [13]: j = lambda i: i**2 + 2*i + 1
In [14]: j
Out[14]: <function __main__.<lambda>>
没有报告错误。
这是一目了然lambda
,它使您可以像在数学中那样将函数直接写入计算机。
我们稍后会看到。
让我们继续深入挖掘“作业”。
如上所示,等号=
适用于简单数据(1 和 'value')类型和简单表达式(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 7 中有 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' 这里有两个名字,函数m
已经有名字了,重复了。
它的格式如下:
In [27]: m = def m(n):
...: if n > 0:
...: return n**2 + 2*n + 1
SyntaxError: invalid syntax
这不是一个聪明的策略,所以错误报告
我们必须删除其中一个,设置一个没有名称的功能。
m = lambda n:n**2 + 2*n + 1
它被称为“匿名函数”
综上所述,
lambda
在一个内联函数中,它使您能够像在数学中那样在一条直线上编写一个函数lambda
是匿名的
希望这可以帮助。
您可以将其视为匿名函数 - 这里有更多信息:Wikipedia - Anonymous Function
因为我在 Visual FoxPro 中工作,所以我很难理解 lambda 表达式,它具有宏替换以及 ExecScript{} 和 Evaluate() 函数,它们似乎服务于相同的目的。
? Calculator(10, 23, "a + b")
? Calculator(10, 23, "a - b");
FUNCTION Calculator(a, b, op)
RETURN Evaluate(op)
使用正式 lambda 的一个明显好处是(我假设)编译时检查:Fox 不会知道您是否在上面输入了文本字符串,直到它尝试运行它。
这对于数据驱动的代码也很有用:您可以将整个例程存储在数据库的备注字段中,然后在运行时对其进行评估。这使您可以调整应用程序的一部分,而无需实际访问源代码。(但这完全是另一个话题。)
这是一个没有名字的函数。例如,在 c# 中,您可以使用
numberCollection.GetMatchingItems<int>(number => number > 5);
返回大于 5 的数字。
number => number > 5
是这里的 lambda 部分。它表示一个函数,它接受一个参数(数字)并返回一个布尔值(数字 > 5)。GetMatchingItems 方法在集合中的所有项目上使用此 lambda 并返回匹配的项目。
例如,在 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]()()();
在 CS 的上下文中,lambda 函数是一个抽象的数学概念,用于解决数学表达式的符号评估问题。在这种情况下,lambda 函数与lambda term相同。
但在编程语言中,情况有所不同。这是一段被声明为“就地”的代码,可以作为“一等公民”传递。这个概念似乎很有用,因此它进入了几乎所有流行的现代编程语言(参见lambda 函数无处不在)。
在计算机编程中,lambda 是一段代码(语句、表达式或它们的一组),它从外部源获取一些参数。它不能总是一个匿名函数——我们有很多方法来实现它们。
我们对表达式、语句和函数有明确的区分,这是数学家所没有的。
编程中的“函数”一词也有所不同——我们有“函数是一系列要执行的步骤”(来自拉丁语“perform”)。在数学中,它与变量之间的相关性有关。
函数式语言试图尽可能地与数学公式相似,并且它们的单词含义几乎相同。但在其他编程语言中,我们有不同的情况。
Lambda 为大家解释:
Lambda 是一个匿名函数。这意味着 lambda 是 Python 中的一个函数对象,之前不需要引用。让我们在这里考虑这段代码:
def name_of_func():
#command/instruction
print('hello')
print(type(name_of_func)) #the name of the function is a reference
#the reference contains a function Object with command/instruction
为了证明我的提议,我打印出返回我们的 name_of_func 类型:
<class 'function'>
一个函数必须有一个接口,但一个接口说明需要包含一些东西。这是什么意思?让我们更接近我们的函数,我们可能会注意到,在函数的名称之外,还有一些我们需要解释的更多细节来理解函数是什么。
常规函数将使用语法"def"定义,然后我们输入名称并使用"()"确定接口,并以语法":"结束定义。现在我们使用指令/命令进入函数体。
所以让我们在这里考虑这段代码:
def print_my_argument(x):
print(x)
print_my_argument('Hello')
在这种情况下,我们运行名为“print_my_argument”的函数并通过接口传递参数/参数。输出将是:
Hello
既然我们知道了什么是函数以及函数的架构是如何工作的,那么我们可以看看匿名函数。让我们在这里考虑这段代码:
def name_of_func():
print('Hello')
lambda: print('Hello')
这些函数对象几乎相同,只是上层常规函数有一个名称,而另一个函数是匿名函数。让我们仔细看看我们的匿名函数,以了解如何使用它。
所以让我们在这里考虑这段代码:
def delete_last_char(arg1=None):
print(arg1[:-1])
string = 'Hello World'
delete_last_char(string)
f = lambda arg1=None: print(arg1[:-1])
f(string)
所以我们在上面的代码中所做的就是再次编写一个常规函数和一个匿名函数。我们已经分配给 var 的匿名函数,这与给该函数命名几乎相同。无论如何,输出将是:
Hello Worl
Hello Worl
为了充分证明 lambda 是一个函数对象并且不只是模仿一个函数,我们在这里运行这段代码:
string = 'Hello World'
f = lambda arg1=string: print(arg1[:-1])
f()
print(type(f))
输出将是:
Hello Worl
<class 'function'>
最后但同样重要的是,您应该知道 python 中的每个函数都需要返回一些东西。如果函数体中没有定义任何内容,则默认返回 None 。看看这里的这段代码:
def delete_last_char(arg1):
print(arg1[:-1])
string = 'Hello World'
x = delete_last_char(string)
f = lambda arg1=string: print(arg1[:-1])
x2 = f()
print(x)
print(x2)
输出将是:
Hello Worl
Hello Worl
None
None
这个问题已经完全回答了,我不想详细说明。我想分享在 rust 中编写数值计算时的用法。
有一个 lambda(匿名函数)的例子
let f = |x: f32| -> f32 { x * x - 2.0 };
let df = |x: f32| -> f32 { 2.0 * x };
当我写牛顿-拉夫森方法的一个模块时,它被用作一阶和二阶导数。(如果您想了解什么是 Newton-Raphson 方法,请访问“ 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);
};
如果我们使用箭头/lambda 函数来完成这种传输会怎样:
// ES6
const food = (kebap, coke) => { return kebap + coke };
您会看到客户没有区别,也没有浪费时间考虑如何发送食物。只是发送它。
顺便说一句,我不推荐带可乐的烤肉串,这就是为什么上面的代码会给你错误。玩得开心。
一个 lambda 函数可以接受任意数量的参数,但它们只包含一个表达式。... Lambda 函数可用于返回函数对象。从语法上讲,lambda 函数仅限于单个表达式。