function - '闭包'和'lambda'之间的区别是什么?

  显示原文与译文双语对照的内容

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

现在我们已经在这里了,它们与普通函数有什么不同?

时间:

不带name,地下定义一个 λ 只是匿名函数- 一个函数 在某些语言中,如 Scheme,它们等价于命名函数。 实际上,函数定义是re-written在内部将一个lambda绑定到一个变量。 在其他语言中,如 python,它们之间有一些( 不必要的) 区别,但它们的行为方式相同。

过去是 defined,一个闭包是任何函数,在其中通过的环境. 关闭 这意味着它可以访问不在参数列表中的变量。 例如:


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

anotherfunc - h 是undefined,这将导致一个错误,因为通过 func 并不紧了环境问题func 只在全局环境上关闭。 这将工作:


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

因为在这里,func 是在 anotherfunc 中定义的,在 python 2.3和更大的( 或者像这样的数字) 中是在它们 几乎几乎 得把这个闭包更正( 突变仍然不起作用),意味着它通过 关闭 anotherfunc 环境并可以访问变量的入其中。 在 python 3.1 + 中,当使用 nonlocal 关键字时,变异也会起作用。

另一个要点- func 将继续在 anotherfunc 环境下关闭,即使它不再在 anotherfunc 中被评估。 这里代码也将工作:


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

print anotherfunc(10)()

这将打印 10.

这一点,当你注意到它们是两个不同的,与 λ 无关- ( 尽管相关) 概念。

当大多数人想到命名函数: 功能,他认为。


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

这些是由名称调用的,当然:


foo();//returns the string 上面

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


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

使用上面的示例,你可以通过指定给它的变量来调用 lambda:

 
foo();

 

然而,比将匿名函数分配给变量更有用的方法是将它们传递给higher-order函数,换句话说,接受/返回其他函数的函数。 在许多情况下,命名函数是不必要的:


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,并不是所有的lambda都是闭包。 两者都是函数,但并不一定以我们用来了解。

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

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

在object-oriented语言中,闭包通常通过对象提供。 然而,一些面向对象语言( 例如。 C# ) 实现与纯粹函数语言 ( 比如 lisp ) 提供的闭包更接近的特殊功能不包含对象的状态。

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

从编程语言的角度看,它们是完全两回事的东西。

对于 图灵完备(Turing-complete) 语言,我们只需要非常有限的元素,比如 抽象,应用和缩减。 抽象和应用程序提供了构建lamdba表达式和减少dertermines表达式意义的方法。

Lambda提供了一种方法,可以将计算过程抽象出来。 例如要计算两个数字的总和,可以提取一个带有两个参数x,y 和返回x+y的进程。 在scheme中,你可以将它的写成


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

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

好,现在想象一下这是如何实现的。 每当我们将lambda表达式应用到一些表达式时,e.g.


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

我们可以简单地将参数替换为要评估的表达式。 这个模型已经非常强大了。 但是这个模型不允许我们改变符号的值,比如 不能模拟状态的变化。 因此我们需要一个更复杂的模型。 简而言之,每当我们想计算lambda表达式的意义时,我们将符号和相应的值放入一个 environment(or table) 。 然后,通过查找表中相应的符号来评估 rest ( + x y ) 。 现在,如果我们提供一些原语直接在环境上操作,我们可以对状态的变化进行建模 !

在这里背景下,检查这里函数:


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

我们知道,当我们计算lambda表达式时,x y 将被绑定到一个新的表中。 但是我们如何和在哪里可以看到z? 实际上z 被称为自由变量。 必须有一个包含z的外部环境。 否则,表达式的含义不能只通过绑定x 和y 来决定。 为了清楚,你可以在scheme中编写如下内容:


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

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

这是你已经很熟悉的东西,一个对象的方法几乎总是依赖于对象的状态。 这就是为什么有些人说"闭包是人类的低劣对象"。 "但是我们也可以把对象看作是人类的糟糕闭包,因为我们真的喜欢第一类函数。

我使用scheme来阐释思想,因为这个方案是最早具有真实闭包的语言之一。 这里的所有材料都在第 3章中得到了更好的展示。

总而言之,lambda和闭包实际上是不同的概念。 lambda是一个函数。 闭包是一对lambda和关闭lambda的相应环境。

概念与上面描述的相同,但如果你是来自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();

在本文中很好地解释了。

就这么简单: lambda是一种语言构造,换句话说,只是匿名函数的语法;闭包是一种实现它的技术,比如--或者匿名函数。

在那个code,具体而言,就是一个闭包是一个 一流的函数是如何在运行时,或者当一对它的通过的所有 non-local"正在关闭""代码"和一个环境变量表达 used. 这样,即使它们源自的外部作用域已经存在,这些变量仍然可以访问。

不幸的是,有很多语言不支持作为一流值的函数,或者只支持以残废形式的函数。 所以人们经常使用"关闭"来区分"真正的事情"。

这个问题太旧了,有很多答案。 现在,Java 8和官方的Lambda是非正式的闭包项目,它就会产生问题。

在java上下文中回答:http://www.lambdafaq.org/lambdas-and-closures-whats-the-difference/ copy-pasting内容:

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

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

...