Лямбда лифтинг
Лямбда-лифтинг — это метапроцесс , который реструктурирует компьютерную программу так, что функции определяются независимо друг от друга в глобальной области видимости . Индивидуальный «лифт» преобразует локальную функцию в глобальную. Это двухэтапный процесс, состоящий из;
- Устранение свободных переменных в функции путем добавления параметров.
- Перемещение функций из ограниченной области в более широкую или глобальную.
Термин «лямбда-лифтинг» был впервые введен Томасом Джонссоном примерно в 1982 году и исторически рассматривался как механизм реализации языков функционального программирования . Он используется в сочетании с другими методами в некоторых современных компиляторах .
Лямбда-подъем — это не то же самое, что преобразование замыкания. Он требует всех сайтов вызовов настройки (добавления дополнительных аргументов к вызовам) и не вводит замыкание для поднятого лямбда-выражения. Напротив, преобразование замыкания не требует настройки сайтов вызовов, но вводит замыкание для лямбда-выражения, отображающего свободные переменные в значения.
Этот метод можно использовать для отдельных функций при рефакторинге кода , чтобы сделать функцию пригодной для использования за пределами области, в которой она была написана. Лямбда-подъемы также можно повторять, чтобы преобразовать программу. Повторяющиеся подъемы можно использовать для преобразования программы, написанной с использованием лямбда-исчисления , в набор рекурсивных функций без лямбда-выражений. Это демонстрирует эквивалентность программ, написанных с помощью лямбда-исчисления, и программ, написанных как функции. [1] Однако это не демонстрирует правильность лямбда-исчисления для вывода, поскольку эта-редукция, используемая в лямбда-подъеме, является шагом, который вводит проблемы мощности в лямбда-исчисление, поскольку оно удаляет значение из переменной без предварительной проверки того, что существует только одно значение, удовлетворяющее условиям переменной (см. парадокс Карри ).
Лямбда-подъем требует много времени для компилятора. Эффективная реализация лямбда-лифтинга о времени обработки компилятором. [2]
В нетипизированном лямбда-исчислении , где основными типами являются функции, подъем может изменить результат бета-редукции лямбда-выражения. Полученные функции будут иметь одно и то же значение в математическом смысле, но не считаются одной и той же функцией в нетипизированном лямбда-исчислении. См. также интенсиональное и экстенсиональное равенство .
Операция, обратная подъему лямбды, — это понижение лямбды . [3]
Удаление лямбда-выражений может ускорить компиляцию программ для компилятора, а также повысить эффективность получаемой программы за счет уменьшения количества параметров и размера кадров стека. Однако это затрудняет повторное использование функции. Отброшенная функция привязана к своему контексту и может использоваться в другом контексте только в том случае, если она была сначала поднята.
Алгоритм
[ редактировать ]Следующий алгоритм представляет собой один из способов лямбда-подъема произвольной программы на языке, который не поддерживает замыкания как объекты первого класса :
- Переименуйте функции так, чтобы каждая функция имела уникальное имя.
- Замените каждую свободную переменную дополнительным аргументом включающей функции и передавайте этот аргумент при каждом использовании функции.
- Замените каждое определение локальной функции, не имеющей свободных переменных, идентичной глобальной функцией.
- Повторяйте шаги 2 и 3, пока все свободные переменные и локальные функции не будут удалены.
Если в языке есть замыкания как объекты первого класса, которые можно передавать в качестве аргументов или возвращать из других функций, замыкание должно быть представлено структурой данных, которая фиксирует привязки свободных переменных.
Пример
[ редактировать ]Следующая программа OCaml вычисляет сумму целых чисел от 1 до 100:
let rec sum n =
if n = 1 then
1
else
let f x =
n + x in
f (sum (n - 1)) in
sum 100
( let rec
заявляет sum
как функция, которая может вызывать сама себя.) Функция f, которая добавляет аргумент sum к сумме чисел, меньших, чем аргумент, является локальной функцией. В рамках определения f n является свободной переменной. Начните с преобразования свободной переменной в параметр:
let rec sum n =
if n = 1 then
1
else
let f w x =
w + x in
f n (sum (n - 1)) in
sum 100
Затем поднимите f в глобальную функцию:
let rec f w x =
w + x
and sum n =
if n = 1 then
1
else
f n (sum (n - 1)) in
sum 100
Ниже приведен тот же пример, на этот раз написанный на JavaScript :
// Initial version
function sum(n) {
function f(x) {
return n + x;
}
if (n == 1)
return 1;
else
return f(sum(n - 1));
}
// After converting the free variable n to a formal parameter w
function sum(n) {
function f(w, x) {
return w + x;
}
if (n == 1)
return 1;
else
return f(n, sum(n - 1));
}
// After lifting function f into the global scope
function f(w, x) {
return w + x;
}
function sum(n) {
if (n == 1)
return 1;
else
return f(n, sum(n - 1));
}
Лямбда-лифтинг против замыканий
[ редактировать ]Лямбда-подъем и закрытие — оба метода реализации блочно-структурированных программ. Он реализует блочную структуру, устраняя ее. Все функции подняты на глобальный уровень. Преобразование замыкания обеспечивает «замыкание», которое связывает текущий кадр с другими кадрами. Преобразование замыкания занимает меньше времени компиляции.
Рекурсивные функции и программы с блочной структурой, с подъемом или без него, могут быть реализованы с использованием реализации на основе стека , которая является простой и эффективной. Однако реализация на основе стекового фрейма должна быть строгой (нетерпеливой) . Реализация на основе стекового фрейма требует, чтобы функции функционировали по принципу «последним пришел — первым вышел» (LIFO). То есть самая последняя функция, начавшая вычисление, должна завершиться первой.
Некоторые функциональные языки (например, Haskell ) реализованы с использованием ленивых вычислений , которые откладывают вычисления до тех пор, пока не потребуется значение. Стратегия ленивой реализации дает программисту гибкость. Ленивая оценка требует задержки вызова функции до тех пор, пока не будет сделан запрос значения, рассчитанного функцией. Одна из реализаций заключается в записи ссылки на «кадр» данных, описывающих вычисление, вместо значения. Позже, когда значение потребуется, кадр используется для его вычисления как раз тогда, когда оно необходимо. Вычисленное значение затем заменяет эталонное значение.
«Кадр» похож на кадр стека , с той разницей, что он не хранится в стеке. Ленивая оценка требует, чтобы все данные, необходимые для расчета, были сохранены в кадре. Если функция «снята», то в кадр нужно записать только указатель функции и параметры функции. Некоторые современные языки используют сборку мусора вместо распределения на основе стека для управления временем существования переменных. В управляемой среде со сбором мусора замыкание записывает ссылки на кадры, из которых можно получить значения. Напротив, расширенная функция имеет параметры для каждого значения, необходимого для расчета.
Пусть выражения и лямбда-исчисление
[ редактировать ]Выражение Let полезно при описании подъема и опускания, а также при описании взаимосвязи между рекурсивными уравнениями и лямбда-выражениями. В большинстве функциональных языков есть выражения let. Кроме того, языки блочного программирования, такие как ALGOL и Pascal, схожи в том, что они также допускают локальное определение функции для использования в ограниченной области .
Использованное здесь выражение let представляет собой полностью взаимно рекурсивную версию let Rec , реализованную во многих функциональных языках.
Пусть выражения относятся к лямбда-исчислению . Лямбда-исчисление имеет простой синтаксис и семантику и хорошо подходит для описания лямбда-подъема. Удобно описывать подъем лямбды как перевод лямбда -выражения в let , а опускание лямбды — как обратный. Это связано с тем, что выражения let допускают взаимную рекурсию, что в некотором смысле более сложно, чем поддерживается в лямбда-исчислении. Лямбда-исчисление не поддерживает взаимную рекурсию, и в самой внешней глобальной области видимости может быть определена только одна функция.
Правила преобразования , описывающие перевод без поднятия, приведены в статье Выражение Let .
Следующие правила описывают эквивалентность лямбда-выражений и выражений let:
Имя | Закон |
---|---|
Эта-редукционная эквивалентность | |
Let-лямбда-эквивалентность | |
Пусть комбинация |
Будут даны метафункции, описывающие подъем и опускание лямбды. Метафункция — это функция, которая принимает программу в качестве параметра. Программа — это данные для метапрограммы. Программа и метапрограмма находятся на разных метауровнях.
Следующие соглашения будут использоваться для того, чтобы отличить программу от метапрограммы:
- Квадратные скобки [] будут использоваться для обозначения применения функции в метапрограмме.
- Для переменных в метапрограмме будут использоваться заглавные буквы. Строчные буквы обозначают переменные в программе.
- будет использоваться для равенства в метапрограмме.
- представляет фиктивную переменную или неизвестное значение.
Для простоты первое правило учитывает совпадения. Правила также предполагают, что лямбда-выражения были предварительно обработаны, поэтому каждая лямбда-абстракция имеет уникальное имя.
Оператор замены широко используется. Выражение означает замену каждого вхождения G в L на S и возврат выражения. Используемое определение расширено и включает замену выражений из определения, данного на странице лямбда-исчисления . Сопоставление выражений должно сравнивать выражения на альфа-эквивалентность (переименование переменных).
Лямбда-подъем в лямбда-исчислении
[ редактировать ]Каждый лямбда-лифтинг принимает лямбда-абстракцию, которая является подвыражением лямбда-выражения, и заменяет ее вызовом функции (приложением) для создаваемой функции. Свободные переменные в подвыражении являются параметрами вызова функции.
Лямбда-лифты можно использовать для отдельных функций при рефакторинге кода , чтобы сделать функцию пригодной для использования за пределами области, в которой она была написана. Такие подъемы также можно повторять до тех пор, пока в выражении не исчезнут лямбда-абстракции, чтобы преобразовать программу.
Лямбда-лифт
[ редактировать ]Подъему присваивается подвыражение внутри выражения для подъема на начало этого выражения. Выражение может быть частью более крупной программы. Это позволяет контролировать, куда поднимается подвыражение. Операция лямбда-подъема, используемая для выполнения подъема в программе:
Подвыражение может быть либо лямбда-абстракцией, либо лямбда-абстракцией, примененной к параметру.
Возможны два типа подъема.
Анонимный лифт имеет выражение лифта, которое представляет собой только лямбда-абстракцию. Это рассматривается как определение анонимной функции. Для функции необходимо создать имя.
Именованное выражение Lift имеет лямбда-абстракцию, примененную к выражению. Этот подъем рассматривается как именованное определение функции.
Анонимный лифт
[ редактировать ]Анонимный подъем использует лямбда-абстракцию (называемую S ). Для С ;
- Создайте имя для функции, которая заменит S (называемую V ). Убедитесь, что имя, указанное V, не использовалось.
- Добавьте параметры в V для всех свободных переменных в S , чтобы создать выражение G (см. make-call ).
Лямбда-лифтинг — это замена лямбда-абстракции S для приложения функции вместе с добавлением определения функции.
В новом лямбда-выражении G что L [ S := G ] означает замену S на G в L. заменен на S. Обратите внимание , В определения функций определение функции G = S. добавлено
В приведенном выше правиле G которое заменяется выражением S. — это приложение функции , Это определяется,
где V — имя функции. Это должна быть новая переменная, т. е. имя, еще не использованное в лямбда-выражении.
где — это метафункция, которая возвращает набор переменных, используемых E. в
Пример анонимного подъема. |
---|
Построение звонка
[ редактировать ]Вызов функции G создается путем добавления параметров для каждой переменной из набора свободных переменных (представленного V ) к функции H ,
Пример построения звонка. |
---|
Именованный лифт
[ редактировать ]Именованный лифт аналогичен анонимному лифту, за исключением того, что имя функции V. указывается
Что касается анонимного лифта, выражение G строится из V путем применения свободных переменных S . Это определяется,
Пример именованного лифта. |
---|
Лямбда-лифт-преобразование
[ редактировать ]Преобразование лямбда-лифта принимает лямбда-выражение и поднимает все лямбда-абстракции в начало выражения. Затем абстракции преобразуются в рекурсивные функции , что устраняет лямбда-абстракции. В результате получается функциональная программа в виде,
где M — серия определений функций, а N — выражение, представляющее возвращаемое значение.
Например,
Затем можно использовать метафункцию de-let для преобразования результата обратно в лямбда-исчисление.
Обработка преобразования лямбда-выражения представляет собой серию подъемов. Каждый лифт имеет,
- Подвыражение, выбранное для него функциейlift -choice . Подвыражение следует выбирать так, чтобы его можно было преобразовать в уравнение без лямбда-выражений.
- Подъем выполняется путем вызова метафункции лямбда-лифт , описанной в следующем разделе.
После установки лифтов квартиры объединяются в одну.
Затем удаление параметров применяется , чтобы удалить параметры, которые не нужны в выражении «let». Выражение let позволяет определениям функций напрямую ссылаться друг на друга, тогда как лямбда-абстракции строго иерархичны, и функция не может напрямую ссылаться на себя.
Выбор выражения для лифтинга
[ редактировать ]Существует два разных способа выбора выражения для подъема. Первый рассматривает все лямбда-абстракции как определяющие анонимные функции. Второй рассматривает лямбда-абстракции, применяемые к параметру, как определение функции. Лямбда-абстракции, применяемые к параметру, имеют двойную интерпретацию: либо как выражение let, определяющее функцию, либо как определяющее анонимную функцию. Обе интерпретации верны.
Эти два предиката необходимы для обоих определений.
без лямбды — выражение, не содержащее лямбда-абстракций.
лямбда-анон — анонимная функция. Выражение вроде где X не содержит лямбды.
Выбор анонимных функций только для подъема
[ редактировать ]Найдите самую глубокую анонимную абстракцию, чтобы при применении подъема поднятая функция стала простым уравнением. Это определение не распознает лямбда-абстракции с параметром как определяющие функцию. Все лямбда-абстракции считаются определяющими анонимные функции.
Lift-Choice — первый аноним, найденный при обходе выражения, или нет , если функции нет.
Например,
Выбор именованных и анонимных функций для подъема
[ редактировать ]Найдите самое глубокое именованное или анонимное определение функции, чтобы при применении подъема поднятая функция стала простым уравнением. Это определение распознает лямбда-абстракцию с фактическим параметром как определение функции. Только лямбда-абстракции без приложения считаются анонимными функциями.
- лямбда-имя
- Именованная функция. Выражение вроде где M не содержит лямбда-выражений, а N не содержит лямбда-выражений или анонимную функцию.
- выбор лифта
- Первая анонимная или именованная функция, найденная при обходе выражения, или нет, если функции нет.
Например,
Примеры
[ редактировать ]Например, комбинатор Y ,
поднимается как,
и после удаления параметра ,
В качестве лямбда-выражения (см. Преобразование let в лямбда-выражения ),
Если поднимаются только анонимные функции, комбинатор Y:
и после удаления параметра ,
В качестве лямбда-выражения
Первое подвыражение, которое будет выбрано для подъема, — это . Это преобразует лямбда-выражение в и создает уравнение .
Второе подвыражение, которое следует выбрать для подъема, — это . Это преобразует лямбда-выражение в и создает уравнение .
И результат:
Удивительно, но этот результат проще, чем результат, полученный при поднятии именованных функций.
Исполнение
[ редактировать ]Примените функцию к K ,
Так,
или
Y-Комбинатор неоднократно вызывает свой параметр (функцию) сам для себя. Значение определяется, если функция имеет фиксированную точку . Но функция никогда не завершится.
Выпадение лямбды в лямбда-исчислении
[ редактировать ]Падение лямбды [4] уменьшает область действия функций и использует контекст из уменьшенной области, чтобы уменьшить количество параметров функций. Уменьшение количества параметров упрощает понимание функций.
В разделе, посвященном подъему лямбда , была описана метафункция для первого подъема, а затем преобразования полученного лямбда-выражения в рекурсивное уравнение. Мета-функция Lambda Drop выполняет обратное, сначала преобразуя рекурсивные уравнения в лямбда-абстракции, а затем помещая полученное лямбда-выражение в наименьшую область видимости, охватывающую все ссылки на лямбда-абстракцию.
Удаление лямбды выполняется в два этапа:
Лямбда падение
[ редактировать ]Отбрасывание Lambda применяется к выражению, которое является частью программы. Удаление контролируется набором выражений, из которых удаление будет исключено.
где,
- L — это лямбда-абстракция, которую нужно отбросить.
- P — программа
- X — набор выражений, которые необходимо исключить из удаления.
Преобразование капли лямбда
[ редактировать ]Преобразование лямбда-отбрасывания погружает все абстракции в выражение. Погружение исключено из выражений в наборе выражений,
где,
- L — выражение, которое необходимо преобразовать.
- X — это набор подвыражений, которые необходимо исключить из удаления.
«sink-tran» поглощает каждую абстракцию, начиная с самой внутренней,
Абстракция тонет
[ редактировать ]Погружение перемещает лямбда-абстракцию внутрь, насколько это возможно, так, чтобы она все еще находилась за пределами всех ссылок на переменную.
Заявление – 4 случая.
Абстракция . Используйте переименование, чтобы гарантировать, что все имена переменных различны.
Переменная – 2 случая.
Тест стока исключает отбрасывание выражений,
Пример
[ редактировать ]Пример затопления |
---|
Удаление параметров
[ редактировать ]Удаление параметра — это оптимизация функции по ее положению в функции. Лямбда-лифтинг добавил параметры, необходимые для того, чтобы функцию можно было вывести из контекста. При удалении этот процесс обратный, и дополнительные параметры, содержащие свободные переменные, могут быть удалены.
Удаление параметра — это удаление ненужного параметра из функции, при этом фактический передаваемый параметр всегда представляет собой одно и то же выражение. Свободные переменные выражения также должны быть свободными там, где определена функция. В этом случае отброшенный параметр заменяется выражением в теле определения функции. Это делает параметр ненужным.
Например, рассмотрим,
В этом примере фактический параметр формального параметра o всегда равен p . Поскольку p — свободная переменная во всем выражении, параметр можно отбросить. Фактический параметр формального параметра y всегда равен n . Однако n связан с лямбда-абстракцией. Поэтому этот параметр нельзя отбрасывать.
Результатом удаления параметра будет:
Для основного примера
Определение drop-params-tran таково:
где,
Списки параметров сборки
[ редактировать ]Для каждой абстракции, определяющей функцию, создайте информацию, необходимую для принятия решения об удалении имен. Эта информация описывает каждый параметр; имя параметра, выражение для фактического значения и указание на то, что все выражения имеют одно и то же значение.
Например, в,
параметры функции g :
Формальный параметр | Все одинаковое значение | Фактическое выражение параметра |
---|---|---|
х | ЛОЖЬ | _ |
тот | истинный | п |
и | истинный | н |
Каждая абстракция переименовывается с уникальным именем, а список параметров связывается с именем абстракции. Например, г есть список параметров.
build-param-lists создает все списки для выражения, просматривая выражение. Он имеет четыре параметра;
- Анализируемое лямбда-выражение.
- Параметр table перечисляет имена.
- Таблица значений параметров.
- Возвращаемый список параметров, который используется внутри
Абстракция — лямбда-выражение формы. анализируется для извлечения имен параметров функции.
Найдите имя и начните создавать список параметров для этого имени, заполняя формальные имена параметров. Также получите любой фактический список параметров из тела выражения и верните его как фактический список параметров из этого выражения.
Переменная — вызов функции.
Для имени функции или параметра начните заполнение фактического списка параметров, выводя список параметров для этого имени.
Приложение . Приложение (вызов функции) обрабатывается для извлечения фактических сведений о параметрах.
Получите списки параметров для выражения и параметра. Получите запись параметра из списка параметров из выражения и проверьте, соответствует ли текущее значение параметра этому параметру. Запишите значение имени параметра для последующего использования при проверке.
Приведенная выше логика довольно тонка в том, как она работает. Тот же индикатор значения никогда не имеет значения true. Значение false устанавливается только в том случае, если все значения не могут быть сопоставлены. Значение извлекается с помощью S для создания набора логических значений, разрешенных S. для Если true является членом, то все значения этого параметра равны, и параметр можно удалить.
Аналогично, def использует теорию множеств для запроса, присвоено ли переменной значение;
Пусть - Пусть выражение.
И - Для использования в "пусть".
Примеры
[ редактировать ]Например, построение списков параметров для,
дает,
и параметр o опускается, чтобы дать:
Создайте список параметров для |
---|
Другой пример:
Здесь x равен f. Сопоставление списка параметров:
и параметр x опускается, чтобы получить:
Создайте список параметров для |
---|
Отбросить параметры
[ редактировать ]Используйте информацию, полученную из списков параметров сборки, чтобы удалить фактические параметры, которые больше не требуются. drop-params имеет параметры,
- Лямбда-выражение, в которое нужно отбросить параметры.
- Сопоставление имен переменных со списками параметров (встроенными в списки параметров сборки).
- Набор переменных, свободных в лямбда-выражении.
- Возвращаемый список параметров. Параметр, используемый внутри алгоритма.
Абстракция
где,
где,
Переменная
Для имени функции или параметра начните заполнение фактического списка параметров, выводя список параметров для этого имени.
Приложение . Приложение (вызов функции) обрабатывается для извлечения
Пусть - Пусть выражение.
И - Для использования в "пусть".
Удаление параметров из приложений |
---|
Отбросьте формальные параметры
[ редактировать ]drop-formal удаляет формальные параметры на основе содержимого выпадающих списков. Его параметры:
- Выпадающий список,
- Определение функции (лямбда-абстракция).
- Свободные переменные из определения функции.
дроп-формальный определяется как,
Что можно объяснить так:
- Если все фактические параметры имеют одинаковое значение и все свободные переменные этого значения доступны для определения функции, отбросьте параметр и замените старый параметр его значением.
- иначе не удаляйте параметр.
- else возвращает тело функции.
Состояние | Выражение |
---|
Пример
[ редактировать ]Начиная с определения функции Y-комбинатора,
Трансформация | Выражение |
---|---|
аннотация * 4 | |
лямбда-абстрактный-тран | |
сток-тран | |
сток-тран | |
отбросить параметр | |
бета-редекс |
Что возвращает комбинатор Y ,
См. также
[ редактировать ]- Пусть выражение
- Комбинатор с фиксированной точкой
- Лямбда-исчисление
- Дедуктивное лямбда-исчисление
- Суперкомбинатор
- Парадокс Карри
Ссылки
[ редактировать ]- ^ Джонсон, Томас (1985). «Лямбда-лифтинг: преобразование программ в рекурсивные уравнения». В Жуанно, JP (ред.). Языки функционального программирования и архитектура компьютера. ФПКА 1985 . Конспекты лекций по информатике. Том. 201. Спрингер. CiteSeerX 10.1.1.48.4346 . дои : 10.1007/3-540-15975-4_37 . ISBN 3-540-15975-4 .
- ^ Морасан, Марко Т.; Шульц, Ульрик П. (2008), «Оптимальный лямбда-лифтинг за квадратичное время», Реализация и применение функциональных языков — переработанные избранные статьи , стр. 37–56, doi : 10.1007/978-3-540-85373-2_3 , ISBN 978-3-540-85372-5
- ^ Дэнви, О.; Шульц, УП (1997). «Лямбда-отбрасывание» . Уведомления ACM SIGPLAN . 32 (12): 90. дои : 10.1145/258994.259007 .
- ^ Дэнви, Оливье; Шульц, Ульрик П. (октябрь 2000 г.). «Отбрасывание лямбда-выражений: преобразование рекурсивных уравнений в программы с блочной структурой» (PDF) . Теоретическая информатика . 248 (1–2): 243–287. CiteSeerX 10.1.1.16.3943 . дои : 10.1016/S0304-3975(00)00054-2 . БРИКС-РС-99-27.
Внешние ссылки
[ редактировать ]- Объяснение переполнения стека на примере JavaScript
- Слоннегер, Кен; Курц, Барри. «5. Некоторое обсуждение выражений let» (PDF) . Основы языка программирования . Университет Айовы.