什么是“Lambda提升”?
我经历了Erlang编译器源代码时遇到了这个问题。
我真的没有得到它。 (去图)),考虑到我刚刚意识到5分钟前有这样的事情)。
请原谅我先问,而不先试着去了解它存在的原因。
有关于它的维基百科文章 ,但它是相当神秘的。
Lambda提升用于将闭包变成纯函数。 通过向函数传递额外的参数,可以减less自由variables的数量。 当你将lambda“提升”到更高和更高的作用域时,你可以添加参数来适应在该作用域中声明的局部variables(否则将是自由variables)。 一旦lambda没有自由variables,它是一个纯粹的“顶级”function。
当然,如果你知道所有的lambda的呼叫站点,你只能做到这一点; 换句话说,只有当lambda不能逃脱。
编译器优化器的好处是可以消除闭包(函数环境)。 这可能使得传递寄存器中的参数成为可能,而不是堆栈(或堆)将它们分配为自由variables。
Lambda提升是一种将lambda“提升”到更高层次(大部分是顶层)的技术。
Doug Currie描述了你为什么要这样做。
下面是一些关于如何手动执行此操作的示例代码(在JavaScript中):
function addFive(nr) { var x = 5; function addX(y) { return x + y; } return addX(nr); }
现在,如果你不想在addFive
的定义里面使用这个addX
函数,你可以像下面这样把它提升到顶层:
function addX(y) { return x + y; } function addFive(nr) { var x = 5; return addX(nr); }
但是,这不起作用,因为x
variables在addX
函数的上下文中不再可用。 解决这个问题的方法是给函数添加一个额外的forms参数:
function addX(y, x) { return x + y; } function addFive(nr) { var x = 5; return addX(nr, x); }
另外:这是一个非常人为的lambda`逃避'的例子。 在那里你不能像我所描述的那样轻松地完成拉姆达操作。
function getAddFiveFunc() { var x = 5; function addX(y) { return x + y; } return addX; }
现在如果有人调用getAddFiveFunc
函数,他们会得到一个函数。 这个函数可以在各种地方使用,现在,如果你想解除addX
函数,你将不得不更新所有这些callites。
警告:我的答案实际上描述了与lambda提升不同的捕获variables。 错误的问题(需要睡眠)。 但是我花了一些时间写这个,所以我讨厌删除它。 把它作为一个社区WIKI留下来。
Lambda提升(通常被称为闭包)是一种无缝地允许从嵌套的lambdaexpression式中访问范围variables的方法。
没有select一种特定的语言,很难进入closures的细节。 在任何语言中,lambda提升的副作用之一就是它倾向于将一个variables的生命周期从一个本地的,短暂的范围扩展到一个更长寿的范围。 通常这是以从堆栈中将variables传送到编译器中的堆的forms出现的。 这是一个非常特定于语言的操作,因此会根据语言产生非常不同的实现。
我将专注于C#,因为这可能是堆栈溢出的读者最常见的语言。 让我们从下面的代码开始。
public Func<int> GetAFunction() { var x = 42; Func<int> lambda1 = () => x; Func<int> lambda2 = () => 42; ... return lambda1; }
在这个例子中,我们创build了2个lambdaexpression式。 在这两种情况下,它都被分配到types为Func的委托实例。 .Net中的所有代表都需要一个真正的函数来支持它们。 因此,C#中的所有lambdaexpression式/匿名函数都被转换成方法定义。
为lambda生成一个函数非常简单。 这是一个孤立的函数,只是返回一个常量值。
public static int RealLambda2() { return 42; }
生成lambda1比较困难。 字面定义如下所示
public static int RealLambda1() { return x; }
这显然不会编译,因为x不可访问。 为了使这个工作,C#编译器必须提升variablesx到闭包。 然后它可以在闭包中返回一个指向函数的指针来满足委托expression式
class Closure1 { int x; public int RealLambda1() { return x; } }
这是一个非常简单的例子,但应该希望详细说明提升的艺术。 不幸的是,魔鬼的细节和情况变得复杂得多。
lambda提升基本上消除了variables,并把它们放到纯函数中,简化了执行。