Подтвердить что ты не робот

Что такое "Lambda Lifting"?

Я просто столкнулся с этим при просмотре источника компилятора Erlang.

Я не понимаю. (go figure;)), учитывая, что я только что понял, что есть такая вещь 5 минут назад).

Простите меня, чтобы спросить сначала, не пытаясь сначала понять причины его существования.

Существует статья wikipedia об этом, но это довольно загадочно.

4b9b3361

Ответ 1

Лямбда-подъем используется, чтобы превратить замыкание в чистую функцию. Передавая дополнительные аргументы функции, вы уменьшаете количество ее свободных переменных. Когда вы "поднимаете" лямбду в более высокие и более высокие области, вы добавляете аргументы для размещения локальных переменных, объявленных в этой области (которые в противном случае были бы свободными переменными). Как только лямбда не имеет свободных переменных, это чистая функция "верхнего уровня".

Конечно, вы можете сделать это, только если знаете все лямбда-сайты; другими словами, только если лямбда не убежит.

Преимущество оптимизатора компилятора заключается в том, что замыкания (функциональные среды) могут быть устранены. Это может позволить передавать аргументы в регистры, а не стек (или кучу), выделяя их как свободные переменные.

Ответ 2

Лямбда-подъем - это метод, чтобы "поднять" лямбды на более высокий уровень (в основном на верхний уровень).

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 `escaping '. Там, где вы не сможете делать лямбда-лифтинг так же легко, как я описал.

function getAddFiveFunc()
{
  var x = 5;
  function addX(y)
  {
    return x + y;
  }

  return addX;
}

Теперь, если кто-то вызовет функцию getAddFiveFunc, они вернут функцию. Эта функция может использоваться во всех местах: теперь, если вы хотите поднять функцию addX, вам придется обновить все эти вызовы.

Ответ 3

Предупреждение: Мой ответ на самом деле описывает захваченные переменные, которые отличаются от лямбда-подъема. Не обращайте внимания на вопрос (нужно спать). Но я потратил немного времени на это, поэтому я не хочу его удалять. Оставил его как сообщество WIKI.

Лямбда-лифтинг, часто называемый закрытием, является способом беспрепятственного доступа к переменным области видимости в пределах вложенного лямбда-выражения.

Трудно попасть в ничтожные детали закрытия, не выбирая конкретный язык. Одним из побочных эффектов лямбда-подъема в любом langauge является то, что он имеет тенденцию продлевать время жизни переменной из локальной, короткоживущей области, до гораздо более длительного срока действия. Обычно это происходит в виде переноса переменной из стека в кучу внутри компилятора. Это очень специфичное для языка действие и поэтому создает очень разные реализации на основе языка.

Я сосредоточусь на С#, поскольку это, вероятно, язык, наиболее распространенный для читателей. Давайте начнем со следующего кода.

public Func<int> GetAFunction() {
  var x = 42;
  Func<int> lambda1 = () => x;
  Func<int> lambda2 = () => 42;
  ...
  return lambda1;
}

В этом примере мы создали 2 лямбда-выражения. В обоих случаях он присваивается экземпляру делегата типа Func. Все делегаты в .Net требуют, чтобы реальная функция поддерживала их где-то. Поэтому под капотом все лямбда-выражения/анонимные функции в С# переводятся в определение метода.

Создание функции для lambda2 довольно прямолинейно. Это изолированная функция, которая просто возвращает постоянное значение.

public static int RealLambda2() { 
  return 42;
}

Генерация lambda1 довольно сложная. Литеральное определение будет выглядеть следующим образом

public static int RealLambda1() {
  return x;
}

Это, очевидно, не будет компилироваться, потому что x недоступен. Чтобы выполнить эту работу, компилятор С# должен поднять переменную x в закрытие. Затем он может вернуть указатель на функцию внутри замыкания, чтобы удовлетворить выражение делегата

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

Это довольно простой пример, но мы, надеюсь, подробно остановимся на искусстве подъема. Дьявол, к сожалению, находится в деталях и становится намного сложнее со сценарием.

Ответ 4

лямбда-подъем в основном исключает переменные и ставит их в чистые функции, упрощая выполнение.