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

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


当前回答

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

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

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

其他回答

Lambda vs闭包

是匿名函数(方法)

闭包是封闭(捕获)其封闭范围内的变量的函数。非本地变量)

Java

interface Runnable {
    void run();
}

class MyClass {
    void foo(Runnable r) {

    }

    //Lambda
    void lambdaExample() {
        foo(() -> {});
    }

    //Closure
    String s = "hello";
    void closureExample() {
        foo(() -> { s = "world";});
    }
}

斯威夫特(结束)

class MyClass {
    func foo(r:() -> Void) {}
    
    func lambdaExample() {
        foo(r: {})
    }
    
    var s = "hello"
    func closureExample() {
        foo(r: {s = "world"})
    }
}

它取决于函数是否使用外部变量来执行操作。

外部变量——定义在函数作用域之外的变量。

Lambda表达式是无状态的,因为它依赖于参数、内部变量或常量来执行操作。 函数<Integer,Integer> lambda = t -> { Int n = 2 返回t * n } 闭包保持状态,因为它使用外部变量(即函数体范围之外定义的变量)以及参数和常量来执行操作。 Int n = 2 函数<Integer,Integer>闭包= t -> { 返回t * n }

当Java创建闭包时,它将变量n与函数一起保存,以便在传递给其他函数或在任何地方使用时可以引用它。

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和闭包。

并不是所有的闭包都是lambdas,也不是所有的lambdas都是闭包。两者都是函数,但不一定是以我们习惯的方式。

lambda本质上是一个内联定义的函数,而不是声明函数的标准方法。lambda可以经常作为对象传递。

闭包是一种函数,它通过引用其主体外部的字段来封闭其周围的状态。封闭状态在闭包调用之间保持不变。

在面向对象语言中,闭包通常是通过对象提供的。然而,一些面向对象语言(如c#)实现的特殊功能更接近于纯函数式语言(如lisp)所提供的闭包的定义,后者没有对象来封装状态。

有趣的是,在c#中引入lambda和闭包使函数式编程更接近主流用法。

当大多数人想到函数时,他们想到的是命名函数:

function foo() { return "This string is returned from the 'foo' function"; }

当然,这些都是按名字命名的:

foo(); //returns the string above

使用lambda表达式,你可以有匿名函数:

 @foo = lambda() {return "This is returned from a function without a name";}

在上面的例子中,你可以通过赋值的变量来调用lambda:

foo();

然而,比将匿名函数分配给变量更有用的是将它们传递给或从高阶函数传递,即接受/返回其他函数的函数。在很多情况下,命名一个函数是不必要的:

function filter(list, predicate) 
 { @filteredList = [];
   for-each (@x in list) if (predicate(x)) filteredList.add(x);
   return filteredList;
 }

//filter for even numbers
filter([0,1,2,3,4,5,6], lambda(x) {return (x mod 2 == 0)}); 

闭包可以是命名函数或匿名函数,但当它“关闭”函数定义范围内的变量时,即闭包仍将引用闭包本身中使用的任何外部变量的环境。这是一个命名闭包:

@x = 0;

function incrementX() { x = x + 1;}

incrementX(); // x now equals 1

这看起来并不多,但如果这都是在另一个函数中,你将incrementX传递给一个外部函数呢?

function foo()
 { @x = 0;

   function incrementX() 
    { x = x + 1;
      return x;
    }

   return incrementX;
 }

@y = foo(); // y = closure of incrementX over foo.x
y(); //returns 1 (y.x == 0 + 1)
y(); //returns 2 (y.x == 1 + 1)

这就是在函数式编程中获得有状态对象的方法。因为不需要命名“incrementX”,你可以在这种情况下使用lambda:

function foo()
 { @x = 0;

   return lambda() 
           { x = x + 1;
             return x;
           };
 }