Compiler construction “什么是”呢;Lambda起重“;?

Compiler construction “什么是”呢;Lambda起重“;?,compiler-construction,functional-programming,Compiler Construction,Functional Programming,我在浏览Erlang编译器源代码时遇到了这个问题 我不太明白。考虑到我5分钟前才意识到有这样的事情) 请原谅我先问,而不是先试图理解它存在的原因 关于这件事有很多疑问,但它相当神秘 Lambda提升用于将闭包转换为纯函数。通过向函数传递额外的参数,可以减少其自由变量的数量。当您将lambda“提升”到越来越高的作用域中时,您将添加参数以适应在该作用域中声明的局部变量(否则将是自由变量)。一旦lambda没有自由变量,它就是一个纯粹的“顶级”函数 当然,你只能这样做,如果你知道所有的lambda的

我在浏览Erlang编译器源代码时遇到了这个问题

我不太明白。考虑到我5分钟前才意识到有这样的事情)

请原谅我先问,而不是先试图理解它存在的原因


关于这件事有很多疑问,但它相当神秘

Lambda提升用于将闭包转换为纯函数。通过向函数传递额外的参数,可以减少其自由变量的数量。当您将lambda“提升”到越来越高的作用域中时,您将添加参数以适应在该作用域中声明的局部变量(否则将是自由变量)。一旦lambda没有自由变量,它就是一个纯粹的“顶级”函数

当然,你只能这样做,如果你知道所有的lambda的呼叫网站;换句话说,只有当lambda没有逃逸时


编译器优化器的好处是可以消除闭包(函数环境)。这可能使在寄存器中传递参数成为可能,而不是将其作为自由变量分配给堆栈(或堆)。

lambda提升基本上消除了变量,并将其放入纯函数中,从而简化了执行

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
变量在
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
函数,你必须更新所有这些调用站点。

警告:我的答案实际上描述了捕获的变量,这与lambda提升不同。误读了问题(需要睡眠)。但我花了一点时间来写这篇文章,所以我不愿意删除它。将其作为社区WIKI保留

Lambda提升,通常称为闭包,是一种无缝地允许从嵌套Lambda表达式中访问范围内变量的方法

如果不选择特定的语言,很难深入了解闭包的细节。在任何语言中,lambda提升的副作用之一是,它倾向于将变量的生存期从局部的、短期的范围延长到更长期的范围。通常,这是以将变量从堆栈传输到编译器内的堆的形式发生的。这是一个非常特定于语言的操作,因此会基于该语言生成非常不同的实现

我将重点介绍C#,因为这可能是stack overflow读者最常用的语言。让我们从下面的代码开始

public Func<int> GetAFunction() {
  var x = 42;
  Func<int> lambda1 = () => x;
  Func<int> lambda2 = () => 42;
  ...
  return lambda1;
}
生成lambda1相当困难。文字定义如下所示

public static int RealLambda1() {
  return x;
}
这显然无法编译,因为x不可访问。为了实现这一点,C#编译器必须将变量x提升到闭包中。然后,它可以返回一个指向闭包内函数的指针,以满足委托表达式的要求

class Closure1 {
  int x;
  public int RealLambda1() {
    return x;
  }
}

这是一个非常简单的例子,但希望能详细说明提升的艺术。不幸的是,问题出在细节上,场景变得更加复杂。

首先阅读闭包——这可能很有用:因此闭包意义上的自由变量是来自其词汇环境(定义范围?)的变量。顶级函数(在erlangs情况下)是模块函数吗?那么模块级函数中已经存在的未绑定变量呢,它也是自由变量吗?谢谢你的详细回答,我刚刚了解了为什么它会加快速度,在简单的JS示例之后意识到了这一点。你也提到了这一点。我仍然不明白为什么你必须知道所有的lambda的呼叫地点,以便执行提升。。。不应该只有定义它的lex env才重要(因为这是免费变量的来源)?您需要知道所有调用站点的原因是,您需要通过添加额外的参数(在我的JavaScript示例中为x)来更新对函数的所有调用。Escape down意味着它被传递给已知函数,这个函数不会让它逃逸。因此,闭包的生存期小于封闭函数的生存期,因此可以对其进行堆栈分配。它可以存储在一个向上逃逸的数据结构中,也可以传递给一个可以让它逃逸的函数,也可以由创建它的函数返回。因此,环境必须在堆上。为什么这会简化执行?很抱歉直截了当的问题。维基百科的文章提到了同样的事情,即提升的目的是加快速度。每一件小事都为我指明了正确的方向。thanks@deepblue(+4年后)因为不需要维护承载自由变量值的环境;所有的自由变量都被转换成参数,因此为了计算/执行函数,我们只需要处理参数。维护环境,使其在创建闭包的作用域退出后长时间保持活动状态,是/可能是一项复杂的任务,具体取决于语言。这个方案非常复杂。非常简单的例子,我现在明白了:)谢谢。所以自由变量被认为是
class Closure1 {
  int x;
  public int RealLambda1() {
    return x;
  }
}