Урок 9. Комбинаторная редукция
TRANSCRIPT
![Page 1: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/1.jpg)
1Кубенский А.А. Функциональное программирование.
Глава 7. Комбинаторная редукция.
Глава 7. Комбинаторная редукция
Задача: избавиться от имен переменных (и, соответственно, лямбда-выражений) введениемчистого комбинаторного программирования, подобно тому, как это делалось в FP-системе.
Введем следующие комбинаторы:
Вычеркиватель (K):
На самом деле тождественный комбинатор – лишний, легко убедиться, что I = S K K.
S K K = λx.K x (K x) = λx.x = I
K = λx.λy.x Распределитель (S): S = λf.λg.λx.f x (g x) Тождество (I): I = λx.x
Для определения семантики этих комбинаторов нет нужды опираться на лямбда-выражения,лучше просто определить правила применения:
S f g x = f x (g x)
K x y = x
I x = xТеперь можно определить правила редукции графов, в которых нет лямбда выражений, а естьтолько применения конструкторов и комбинаторов. Важно, что при этом можно избежатькопирования, однако интенсивно используются вершины-синонимы, так как по крайней мередва из трех введенных комбинаторов являются чистыми селекторами.
![Page 2: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/2.jpg)
2Кубенский А.А. Функциональное программирование.
Покажем, что в самом деле можно любое замкнутое лямбда-выражение представить вкомбинаторной форме. Для этого сначала введем функцию абстрагирования abs(x, E) для комбинаторных выражений E такую, что
Правила абстрагирования от переменных
Глава 7. Комбинаторная редукция.
abs(x, E) x = E
Другими словами, abs(x, E) ведет себя точно так же, как λx.E.
Сокращенная запись для abs(x, E) – [x]E.
Очевидно, что:
[x]x = I
[x]c = K c
[x]y = K y
[x]e1 e2 = S ([x]e1) ([x]e2)S ([x]e1) ([x]e2) x = ([x]e1) x (([x]e2) x) = e1 e2
Теперь перевод любого лямбда выражения в комбинаторную форму можно производить последующим правилам:
comb(c) = c
comb(v) = v
comb(λx.e) = [x]comb(e)
comb(e1 e2) = comb(e1) comb(e2)
![Page 3: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/3.jpg)
3Кубенский А.А. Функциональное программирование.
Пример перевода выражения в комбинаторную форму
Глава 7. Комбинаторная редукция.
Рассмотрим следующую функцию: λx.λy.+ x y. Заметим, что для этой функции существуеточень простая комбинаторная форма: +. Проведем преобразование этого лямбда-выражения.
comb(λx.λy.+ x y)= [x][y]comb(+ x y)= [x][y]comb(+) comb(x) comb(y)= [x][y]+ x y= [x]S ([y]+ x) ([y]y)= [x]S (S ([y]+) ([y]x)) ([y]y)= [x]S (S (K +) (K x)) I= S ([x]S (S (K +) (K x))) ([x]I)= S (S ([x]S) ([x](S (K +) (K x)))) (K I)= S (S (K S) (S ([x](S (K +)) ([x](K x))))) (K I)= S (S (K S) (S (S ([x]S) ([x](K +))) (S ([x]K) ([x]x)))) (K I)= S (S (K S) (S (S (K S) (S ([x]K) ([x]+))) (S (K K) I))) (K I)= S (S (K S) (S (S (K S) (S (K K) (K +))) (S (K K) I))) (K I)
Это самое сложное выражение для функции сложения в курсе. Проверим, что при примененииэтого выражения к константам, скажем, 2 и 3, мы действительно получим 5
![Page 4: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/4.jpg)
4Кубенский А.А. Функциональное программирование.
Проверка правильности преобразования в комбинаторную форму.
Глава 7. Комбинаторная редукция.
S (S (K S) (S (S (K S) (S (K K) (K +))) (S (K K) I))) (K I) 2
= (S (K S) (S (S (K S) (S (K K) (K +))) (S (K K) I))) 2 ((K I) 2)
= S (K S) (S (S (K S) (S (K K) (K +))) (S (K K) I)) 2 (K I 2)
= (K S) 2 (S (S (K S) (S (K K) (K +))) (S (K K) I) 2) I
= K S 2 ((S (K S) (S (K K) (K +))) 2 (S (K K) I 2)) I
= S (S (K S) (S (K K) (K +)) 2 (K K 2 (I 2))) I
= S (K S 2 (S (K K) (K +) 2) (K 2)) I
= S (S (K K 2 (K + 2)) (K 2)) I
= S (S (K +) (K 2)) I
Здесь видно, как атомы «+» и «2» привязаны к комбинаторам K, «ожидая» применения ихко второму аргументу.
S (S (K +) (K 2)) I 3= S (K +) (K 2) 3 (I 3)= K + 3 (K 2 3) 3= + 2 3= 5
![Page 5: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/5.jpg)
5Кубенский А.А. Функциональное программирование.
Оптимизация Карри
Глава 7. Комбинаторная редукция.
Если хочется упростить сложное комбинаторное выражение, то можно попробовать применитьэквивалентные преобразования выражений. Введем следующие правила.
S (K E1) (K E2) = K (E1 E2) (правило 1)
S (K E1) I = E1 (правило 2)
В корректности этих преобразований можно убедиться, установив, что при применении к одномуи тому же аргументу получается один и тот же результат.
S (K E1) (K E2) X = K E1 X (K E2 X) = E1 E2 = K (E1 E2) X
S (K E1) I X = K E1 X (I X) = E1 X
Еще один путь снизить сложность выражений – ввести дополнительные комбинаторы, например:
B F G X = F (G X) (композитор)
C F X Y = F Y X (перестановщик).
S (K E1) E2 = B E1 E2 (правило 3)
S E1 (K E2) = C E1 E2 (правило 4)
и дополнительные оптимизационные правила:
S (K E1) E2 X = K E1 X (E2 X) = E1 (E2 X) = B E1 E2 X
S E1 (K E2) X = E1 X (K E2 X) = E1 X E2 = C E1 E2 X
в справедливости которых можно убедиться путем следующих преобразований:
![Page 6: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/6.jpg)
6Кубенский А.А. Функциональное программирование.
Оптимизация Карри
Глава 7. Комбинаторная редукция.
Попробуем проверить правила (1 – 4) на практике, преобразовав наше длинное выражение дляфункции сложения.
S (K E1) (K E2) = K (E1 E2) (правило 1)S (K E1) I = E1 (правило 2)S (K E1) E2 = B E1 E2 (правило 3)S E1 (K E2) = C E1 E2 (правило 4)
S (S (K S) (S (S (K S) (S (K K) (K +))) (S (K K) I))) (K I)= S (S (K S) (S (S (K S) (K (K +)) (S (K K) I))) (K I)
(правило 1)
(правило 2)
= S (S (K S) (S (S (K S) (K (K +)) K)) (K I) (правило 1)
= S (S (K S) (S (K (S (K +))) K)) (K I) (правило 4)
= C (S (K S) (S (K (S (K +))) K)) I (правило 3)
= C (B S (S (K (S (K +))) K)) I (правило 3)
= C (B S (B (S (K +)) K)) I
S (S (K +) (K 2)) I
Это выражение уже значительно короче, хотя и далеко от оптимального «+». Проверьте, чтопосле применения этого выражения к 2 получится то же, выражение, которое мы уже получалираньше
![Page 7: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/7.jpg)
7Кубенский А.А. Функциональное программирование.
Оптимизация Карри (продолжение)
Глава 7. Комбинаторная редукция.
Правила оптимизации и новые комбинаторы можно ввести непосредственно в алгоритмпреобразования лямбда-выражений в комбинаторную форму.
При применении этих правил преобразования могут дать гораздо более простой результат.
[x]x = I
[x]c = K c
[x]y = K y
[x]e1 e2 | FST == K M && SND == I = M | FST == K M && SND == K N = K (M N) | FST == K M = B M SND | SND == K N = C FST N | otherwise = S FST SND where FST = [x]e1 SND = [x]e2
comb(λx.λy.+ x y) = [x][y]comb(+ x y)= [x][y]comb(+) comb(x) comb(y) = [x][y]+ x y
[y]+ = K +, [y]x = K x поэтому [y]+ x = K (+ x)
= K [x]+ x = + поскольку [x]+ = K +, [x]x = I
![Page 8: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/8.jpg)
8Кубенский А.А. Функциональное программирование.
Аппликативные подвыражения
Глава 7. Комбинаторная редукция.
Заметим, что если в исходном лямбда-выражении уже имелись подвыражения, имеющиеаппликативный вид (e1 e2), то при его абстрагировании от переменной, не входящей в e1 e2свободно, мы можем и не получить «естественную» форму e1 e2. Например,
Лемма 1: при применении оптимизационного правила (1) для аппликативного выражения e
comb(λx.+ 1 2) = [x]comb(+ 1 2) = [x]+ 1 2= S (S (K +) (K 1)) (K 2)
при неоптимизированных преобразованиях. Компоненты аппликативного подвыраженияраспределяются по всему выражению.
Если применять правило (1) в оптимизации Карри, то выражение приобретет вид
K (+ 1 2)
Сохранение аппликативных подвыражений важно для оптимизации вычислений, посколькупозволяет разделять такие подвыражения более эффективно и сохранять «полную ленивость».
при условии, что x не входит свободно в e.
Доказательство (по индукции): если e – константа или переменная (не равная x), тоутверждение леммы справедливо по определению абстрагирования.
[x]e = K e
Если e = e1 e2, то
[x]e = [x]e1 e2 = S ([x]e1) ([x]e2) = S (K e1) (K e2) = = K (e1 e2) = K e
![Page 9: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/9.jpg)
9Кубенский А.А. Функциональное программирование.
Аппликативные подвыражения
Глава 7. Комбинаторная редукция.
Лемма 2: если e1 является подвыражением аппликативного выражения e, то при вычислении[x]e с применением оптимизационного правила (1) e1 будет также подвыражением [x]e приусловии, что x не входит в e1 свободно.
Таким образом, справедлива теорема: при применении только правила (1) сохраняются всеаппликативные подвыражения исходного лямбда-выражения. Это утверждение такженесложно доказать по индукции.
Доказательство также несложно провести по индукции по структуре выражения e.
Действительно, comb(e) = e, если e – переменная или константа, а также, если оно самоявляется аппликативным выражением.
Далее, comb(e1 e2) = comb(e1) comb(e2), и аппликативное подвыражение сохраняетсяпо индукционному предположению.Наконец, comb(λx.e) = [x]comb(e), и аппликативное подвыражение сохраняетсяпо лемме (2).
![Page 10: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/10.jpg)
10Кубенский А.А. Функциональное программирование.
В графах для комбинаторных выражений не будет лямбда-вершин, только вершины-константы,вершины-примитивы (включая базовые комбинаторы), и вершины-конструкторы.
Комбинаторная редукция графов
Глава 7. Комбинаторная редукция.
Преобразования таких графов (редукции) будут происходить с помощью применения «дельта-правил», которые надо определить в том числе и для всех базовых комбинаторов. Приятно,что при этом можно полностью избежать копирования.
Если аргументов для применения «дельта-редукции» недостаточно, то граф уже находитсяв СЗНФ. Правила применения базовых комбинаторов выглядят так:
@
I ee
@
e2e1
@
K e1
@
e3@
e2@
S e1
@
e3
@ e2
@
e1
![Page 11: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/11.jpg)
11Кубенский А.А. Функциональное программирование.
Комбинаторная редукция графов
Глава 7. Комбинаторная редукция.
Правила применения дополнительных базовых комбинаторов B и C выглядят так:
@
e3@
e2@
C e1
@
e3
@ e2
e1
@
e3@
e2@
B e1
@
e3e2
@e1
Эти правила приведены в самом общем виде (без использования оптимизаций).
Мы исключили копирование, но при применении функций проекторов (в частности, I и K)придется по-прежнему создавать вершины-синонимы, если на аргументы есть ссылки.
![Page 12: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/12.jpg)
12Кубенский А.А. Функциональное программирование.
Рекурсия
Глава 7. Комбинаторная редукция.
До сих пор мы рассматривали только выражения, не содержащие рекурсию. Рекурсию можнореализовать, если ввести в рассмотрение Y-комбинатор. Однако, если рассмотретьY-комбинатор Карри в его лямбда-выражении Y = λh.(λx.h (x x)) (λx.h (x x)), тооказывается, что его комбинаторное представление comb(Y) слишком сложное.
На самом деле рекурсия здесь использована «не по существу», но сути дела это не меняет.
Теперь построим графы этих выражений и выражения f 2, соединяя в месте «рекурсивных»вызовов.
Для непосредственно выраженной «плоской» рекурсии можно предложить два подхода:• зацикливание графов для рекурсивных выражений;• непосредственное введение правил преобразования графов для Y-комбинатора.
Пусть имеются две взаимно рекурсивные функции в следующем выражении:
letrec f = λx.cons x (g x) g = λx.cons (sqr x) (f x)in f 2
Прежде всего преобразуем все выражения в комбинаторную форму.
f = comb(λx.cons x (g x)) = [x](cons x (g x)) = S cons gg = [x]cons (sqr x) (f x) = S (B cons sqr) f
![Page 13: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/13.jpg)
13Кубенский А.А. Функциональное программирование.
Рекурсия
Глава 7. Комбинаторная редукция.
Этот граф содержит циклические связи, однако его преобразование происходит по введеннымнами ранее правилам точно так же, как и для нециклических графов.
f = comb(λx.cons x (g x)) = [x](cons x (g x)) = S cons gg = [x]cons (sqr x) (f x) = S (B cons sqr) ff 2
@
sqr
@
cons
@
S
B
@
g@
S cons
@
f
2f
@
f 2
gf
Заметим, впрочем, что результатом работы будет бесконечный список.
![Page 14: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/14.jpg)
14Кубенский А.А. Функциональное программирование.
Пример преобразования циклического графа
Глава 7. Комбинаторная редукция.
@
@
S cons @
sqr
@
cons
@
S
B
@
2
@
@
@
@
:
В ленивой схеме вычислений на этом вычисление заканчивается (получен циклический список)
![Page 15: Урок 9. Комбинаторная редукция](https://reader036.vdocuments.site/reader036/viewer/2022082705/55b750babb61eb8f358b4782/html5/thumbnails/15.jpg)
15Кубенский А.А. Функциональное программирование.
Графическое представление Y-комбинатора
Глава 7. Комбинаторная редукция.
f = λx.letrec g = e1 in e2
Пусть имеем следующее определение функции.
где в выражении e1 имеются рекурсивные ссылки на g, а в выражении e2 – как на f, так и на g.
Из-за вложенной рекурсии трудно выполнить преобразование этого выражения в комбинаторнуюформу непосредственно, однако, можно воспользоваться Y-комбинатором.
f = Y λf.λx.(λg.e2)(Y λg.e1)
comb(f) = comb(Y λf.λx.(λg.e2)(Y λg.e1)) = Y [f][x]([g]comb(e2)) (Y [g]comb(e1))
Правило для «редукции» Y-комбинатора выглядит несколько необычно, так как создаетциклический граф, однако, это вполне адекватная форма вычислений на графах.
@
eY
@
e
Для прямой рекурсии в общем случае правило преобразования выглядит так:
comb(f = e) = Y [f]e