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

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


当前回答

概念与上面描述的相同,但如果您有PHP背景,则使用PHP代码进一步解释。

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, function ($v) { return $v > 2; });

函数($v){返回$v > 2}是lambda函数定义。我们甚至可以将它存储在一个变量中,这样它就可以被重用:

$max = function ($v) { return $v > 2; };

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max);

现在,如果您想更改筛选数组中允许的最大数量,该怎么办?你必须编写另一个lambda函数或创建一个闭包(PHP 5.3):

$max_comp = function ($max) {
  return function ($v) use ($max) { return $v > $max; };
};

$input = array(1, 2, 3, 4, 5);
$output = array_filter($input, $max_comp(2));

闭包是在自己的环境中求值的函数,该环境具有一个或多个绑定变量,在调用该函数时可以访问这些变量。它们来自函数式编程领域,其中有许多概念。闭包类似于lambda函数,但更聪明的地方在于它们能够与定义闭包的外部环境中的变量进行交互。

下面是一个简单的PHP闭包示例:

$string = "Hello World!";
$closure = function() use ($string) { echo $string; };

$closure();

这篇文章解释得很好。

其他回答

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

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

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

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

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

Lambda表达式只是一个匿名函数。例如,在纯java中,你可以这样写:

Function<Person, Job> mapPersonToJob = new Function<Person, Job>() {
    public Job apply(Person person) {
        Job job = new Job(person.getPersonId(), person.getJobDescription());
        return job;
    }
};

其中Function类是在java代码中构建的。现在你可以在某处调用mapPersonToJob.apply(person)来使用它。这只是一个例子。在没有语法之前,这是一个lambda。lambda是一个捷径。

关闭:

当Lambda可以访问此作用域之外的变量时,它就成为闭包。我猜你可以说它的魔力,它神奇地包裹着它被创建的环境,并使用其作用域之外的变量。因此,闭包意味着lambda可以访问它的OUTER SCOPE。

在Kotlin中,lambda总是可以访问它的闭包(在它的外部作用域中的变量)

这个问题已经12年了,我们仍然把它作为谷歌中“闭包vs lambda”的第一个链接。 所以我不得不说,因为没有人明确地说过。

Lambda表达式是一个匿名函数(声明)。

一个闭包,引用Scott的《编程语言语用学》解释为:

创建引用环境的显式表示(通常是当前调用子例程时执行的环境),并将其与子例程的引用捆绑在一起,称为闭包。

也就是说,它就像我们所说的“函数+投降上下文”的捆绑。

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

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;
           };
 }

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

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

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