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