有人能解释一下吗?我理解它们背后的基本概念,但我经常看到它们互换使用,我感到困惑。

现在我们到了这里,它们和普通函数有什么不同?


当前回答

简单地说,闭包是一个关于作用域的技巧,lambda是一个匿名函数。我们可以用lambda更优雅地实现闭包,lambda经常被用作传递给更高函数的参数

其他回答

这个问题很老了,有很多答案。 现在有了Java 8和官方Lambda这两个非官方的闭包项目,这个问题又重新出现了。

Java上下文中的答案(通过Lambdas和闭包-有什么区别?):

闭包是一个lambda表达式,它与一个环境配对,将它的每个自由变量绑定到一个值。在Java中,lambda表达式将通过闭包的方式实现,因此这两个术语在社区中可以互换使用。”

Lambda是一个匿名函数定义,它(不一定)绑定到标识符。

“匿名函数起源于阿朗佐·丘奇(Alonzo Church)发明的lambda微积分,其中所有函数都是匿名的”——维基百科

闭包是lambda函数的实现。

Peter J. Landin在1964年将闭包定义为拥有一个环境部分和一个控制部分,由他的SECD机器用于计算表达式

Lambda和闭包的一般解释在其他响应中涵盖。

对于c++背景的人来说,Lambda表达式是在c++ 11中引入的。可以把Lambdas看作是一种创建匿名函数和函数对象的方便方法。

"The distinction between a lambda and the corresponding closure is precisely equivalent to the distinction between a class and an instance of the class. A class exists only in source code; it doesn’t exist at runtime. What exists at runtime are objects of the class type. Closures are to lambdas as objects are to classes. This should not be a surprise, because each lambda expression causes a unique class to be generated (during compilation) and also causes an object of that class type, a closure to be created (at runtime)." - Scott Myers

c++允许我们检查Lambda和Closure之间的细微差别,因为您必须显式地指定要捕获的自由变量。

在下面的示例中,Lambda表达式没有自由变量,是一个空捕获列表([])。它本质上是一个普通函数,严格意义上不需要闭包。所以它甚至可以作为函数指针参数传递。

void register_func(void(*f)(int val))   // Works only with an EMPTY capture list
{
    int val = 3;
    f(val);
}
 
int main() 
{
    int env = 5;
    register_func( [](int val){ /* lambda body can access only val variable*/ } );
}

只要在捕获列表([env])中引入了来自周围环境的自由变量,就必须生成一个Closure。

    register_func( [env](int val){ /* lambda body can access val and env variables*/ } );

由于这不再是一个普通的函数,而是一个闭包,因此会产生编译错误。 不存在从"lambda []void (int val)->void"到"void (*)(int val)"的合适转换函数

这个错误可以用函数包装器std::function来修复,它接受任何可调用的目标,包括生成的闭包。

void register_func(std::function<void(int val)> f)

有关c++示例的详细解释,请参阅Lambda和闭包。

它很简单:lambda是一种语言结构,即匿名函数的简单语法;闭包是一种实现它的技术——或者任何一类函数,无论是命名的还是匿名的。

更准确地说,闭包是一类函数在运行时如何表示的,作为它的一对“代码”和一个环境在该代码中使用的所有非局部变量上的“闭包”。这样,即使它们产生的外部作用域已经退出,这些变量仍然可以访问。

不幸的是,有许多语言不支持函数作为第一类值,或者只支持残差形式的函数。所以人们经常用“闭合”这个词来区分“实物”。

lambda只是一个匿名函数——一个没有名字定义的函数。在Scheme等某些语言中,它们等同于命名函数。实际上,函数定义在内部被重写为将lambda绑定到变量。在其他语言中,如Python,它们之间有一些(相当不必要的)区别,但它们在其他方面的行为是相同的。

闭包是在定义它的环境上关闭的任何函数。这意味着它可以访问不在其参数列表中的变量。例子:

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

这将导致一个错误,因为func不会在另一个环境中关闭func - h是undefined。Func只在全局环境下关闭。这是可行的:

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

因为这里,func是在anotherfunc中定义的,而在python 2.3及以上版本(或类似这样的数字)中,当他们几乎正确地获得闭包时(突变仍然无效),这意味着它在anotherfunc的环境中关闭,并可以访问其中的变量。在Python 3.1+中,使用nonlocal关键字时也可以使用突变。

还有一点很重要——func将继续在另一个func的环境中关闭,即使它不再在另一个func中被求值。这段代码也可以工作:

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

print anotherfunc(10)()

这将输出10。

正如您所注意到的,这与lambda无关——它们是两个不同(尽管相关)的概念。

从编程语言的角度来看,它们完全是两种不同的东西。

基本上,对于图灵完备语言,我们只需要非常有限的元素,例如抽象、应用和还原。抽象和应用提供了构建lambda表达式的方法,而约简决定了lambda表达式的含义。

Lambda提供了一种将计算过程抽象出来的方法。 例如,要计算两个数字的和,可以抽象出一个接受两个参数x、y并返回x+y的过程。在scheme中,可以写成

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

您可以重命名参数,但它完成的任务不会改变。 在几乎所有的编程语言中,您都可以为lambda表达式指定一个名称,即命名函数。但是没有太大的区别,它们在概念上可以被认为只是语法糖。

好,现在想象一下这是如何实现的。当我们将lambda表达式应用于某些表达式时,例如。

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

We can simply substitute the parameters with the expression to be evaluated. This model is already very powerful. But this model doesn't enable us to change the values of symbols, e.g. We can't mimic the change of status. Thus we need a more complex model. To make it short, whenever we want to calculate the meaning of the lambda expression, we put the pair of symbol and the corresponding value into an environment(or table). Then the rest (+ x y) is evaluated by looking up the corresponding symbols in the table. Now if we provide some primitives to operate on the environment directly, we can model the changes of status!

有了这个背景,检查这个函数:

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

我们知道,当我们求lambda表达式的值时,x y将被绑定到一个新的表中。但是我们怎样才能查到z呢?实际上z是一个自由变量。一定有一个外在的 否则,表达式的含义不能仅通过绑定x和y来确定。为了清楚地说明这一点,可以在scheme中这样写:

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

所以z在外层表中被绑定为1。我们仍然得到一个接受两个参数的函数,但它的真正含义也取决于外部环境。 换句话说,外部环境对自由变量关闭。在set的帮助下!,我们可以使函数有状态,也就是说,它不是数学意义上的函数。它的返回值不仅取决于输入,还取决于z。

这是你们已经非常了解的东西,对象的方法几乎总是依赖于对象的状态。这就是为什么有人说“闭包是穷人的东西”。但我们也可以把对象看作穷人的闭包,因为我们真的喜欢第一类函数。

我用scheme来说明这个想法,因为scheme是最早的有真正闭包的语言之一。这里的所有材料在SICP第3章中都有更好的呈现。

总之,lambda和闭包是完全不同的概念。A是一个函数。闭包是一对lambda和对应的闭包环境。