Кубенский А.А. Функциональное программирование.
description
Transcript of Кубенский А.А. Функциональное программирование.
1Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
4.2. Рекурсия в лямбда-исчислении
fac = λn.(if (= n 0) 1 (* n (fac (- n 1))))
FB = λf.λn.(if (= n 0) 1 (* n (f (- n 1))))
Найти fac такой, что fac = FB fac
Существует ли функция Y такая, что fac = Y FB ? Y F = F (Y F)
Y-комбинатор; комбинатор фиксированной точки.
Лямбда-выражение для Y-комбинатора: Y = λh.(λx.h (x x))(λx.h (x x))
Y F(λx.F (x x))(λx.F (x x))F ((λx.F (x x))(λx.F (x x)))F (Y F)
Тогда функция fac может быть записана с помощью выраженияY FB(λh.(λx.h (x x))(λx.h (x x))) (λf.λn.(if (= n 0) 1 (* n (f (- n 1)))))
Это выражение неприводимо к нормальной форме!
2Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
Пример работы рекурсии, выраженной с помощью Y-комбинатора
fac 2
(Y FB) 2
FB ((Y FB)) 2 FB = λf.λn.(if (= n 0) 1 (* n (f (- n 1))))
(λn.(if (= n 0) 1 (* n ((Y FB) (- n 1))))) 2
if (= 2 0) 1 (* 2 ((Y FB) (- 2 1)))
* 2 ((Y FB) (- 2 1))
* 2 ((λn.(if (= n 0) 1 (* n ((Y FB) (- n 1))))) (- 2 1))
* 2 (if (= (- 2 1) 0) 1 (* (- 2 1) ((Y FB) (- (- 2 1) 1))))
* 2 (* 1 ((Y FB) (- (- 2 1) 1)))
* 2 (* 1 ((λn.(if (= n 0) 1 (* n ((Y FB) (- n 1))))) (- (- 2 1) 1)))
* 2 (* 1 (if (= 0 0) 1 (* (- (- 2 1) 1) ((Y FB) (- (- (- 2 1) 1) 1)))))
* 2 (* 1 1)
2
3Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
Взаимно рекурсивные функции
f1 = F1(f1,...,fn)f2 = F2(f1,...,fn)
fn = Fn(f1,...,fn)...
T = (f1,...,fn)
TUPLE-n – набор функций кортежирования
T = TUPLE-n f1 ... fn
INDEX – функция индексации
fi = INDEX i T
T = TUPLE-n F1(INDEX 1 T,..., INDEX n T) ... Fn(INDEX 1 T,..., INDEX n T)
Это обычная функция с прямой рекурсией, которая может быть выражена с помощью Y-комбинатора:
Y (λT.TUPLE-n F1 ... Fn)
а каждая из функций fi получается применением к этому выражению функции индексации:
fi = INDEX i (Y (λT.TUPLE-n F1 ... Fn))
4Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
4.3. Чистое лямбда-исчисление
Чистое лямбда-исчисление – это исчисление без констант и встроенных функций.Соответственно, отсутствует и δ-редукция; есть только λ-выражения и β-редукция.
Покажем, что выразительная сила чистого лямбда-исчисления не меньше, чем утрадиционного функционального языка программирования.
1. Функция IF, логические значения и булевы функции.IF p e1 e2 p e1 e2TRUE e1 e2 e1FALSE e1 e2 e2
TRUE = λx.λy.xFALSE = λx.λy.yIF = λp.λx.λy.p x yAND = λp.λq.p q FALSEOR = λp.λq.p TRUE qNOT = λp.p FALSE TRUE
5Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
2. Списки и основные функции обработки списков.
4.3. Чистое лямбда-исчисление (продолжение)
NULL (CONS A B) FALSE NULL = λt.t (λx.λy.FALSE)NULL NIL TRUE NIL = λx.TRUE
проверка: NULL NIL (λt.t (λx.λy.FALSE))(λx.TRUE) (λx.TRUE)(λx.λy.FALSE) TRUE
CONS A B λs.s A B CONS = λx.λy.λs.s x yCAR (λs.s A B) ACDR (λs.s A B) B
CAR = λt.t TRUECDR = λt.t FALSE
проверка: CAR (CONS A B) CAR ((λx.λy.λs.s x y) A B) CAR (λs.s A B) (λt.t TRUE)(λs.s A B) (λs.s A B) TRUE TRUE A B A
Дополнительные функции обработки списков
JOIN = λp.λq.IF (NULL p) q (CONS (CAR p) (JOIN (CDR p) q))избавляемся от явной рекурсии с помощью Y-комбинатораJOIN = Y (λf.λp.λq.IF (NULL p) q (CONS (CAR p) (f (CDR p) q)))
6Кубенский А.А. Функциональное программирование.Глава 4. Основы лямбда-исчисления.
3. Целые числа и арифметика.3а. Представление в виде списков (подсчет элементов списка).
4.3. Чистое лямбда-исчисление (продолжение)
0 = NILSUCC = λn.CONS NIL nPRED = CDR
n = (CONS NIL (CONS NIL (... NIL)))
n разEQ0 = NULLНа базе этих основных функций можно построить всю арифметику.
3б. Функциональное представление (подсчет применений функции).
0 = λf.λx.x n = λf.λx.(f (f (... x)))
n разSUCC = λn.λf.λx.(f (n f x))PLUS = λm.λn.(m SUCC n)MULT = λm.λn.(m (PLUS n) 0)EQ0 = λn.(n (λx.FALSE) TRUE)PRED = λn.λf.λx.(n (λg.λh.(h (g f))) (λu.x) (λu.u))
проверка: PRED 0 λf.λx.(0 (λg.λh.(h (g f))) (λu.x) (λu.u)) λf.λx.((λu.x) (λu.u)) λf.λx.x 0
Проверим, что PRED 1 = 0:
7
PRED = λn.λf.λx.(n (λg.λh.(h (g f))) (λu.x) (λu.u)) 1 = λf.λx.(f x)
Вычитание
PRED 1 = λf.λx.((λf.λx.(f x)) (λg.λh.(h (g f))) (λu.x) (λu.u)) PRED 1 = λf.λx.(((λg.λh.(h (g f))) (λu.x)) (λu.u)) PRED 1 = λf.λx.((λh.(h ((λu.x) f))) (λu.u)) PRED 1 = λf.λx.((λh.(h x)) (λu.u)) PRED 1 = λf.λx.((λu.u) x)PRED 1 = λf.λx.x = 0
Преимущество функционального представления состоит в том, что основные арифметические.операции и операции сравнения удается записать без рекурсии.
8Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
Глава 5. Системы исполнения функциональных программ5.1. Интерпретаторы и компиляторы.
Программа(функция)
Аргументы
Результатприменения
функцииИнтерпретатор
Схема интерпретации
Исходнаяпрограмма(функция)
Компилятор
Результирующая(эквивалентная)
программа
Схема компиляции
В обоих случаях исходная функция служит в качестве входных данных;в случае компиляции результатом также является функция.
Для получения результата необходима интерпретацияаппаратным или программным интерпретатором.
9Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
5.2. Представление функциональных программ.
Вводим простой функциональный язык – расширенное лямбда-исчисление;покажем, что все основные конструкции языка типа Haskell могут быть легко скомпилированы в это расширенное лямбда-исчисление.
1. Константы – целые числа, логические значения. c : 0 25 TRUE
2. Константы – примитивные функции. f : + = IF TUPLE-n INDEX
3. Лямбда-выражения. (λx.e) : λx.+ x x
4. Применение примитивной функции или лямбда-выражения к аргументу.
(e1 e2) : (λx.+ x x) 2
5. Простой блок. (let x = e1 in e2) :let f = (λx.+ x x) in (f 2)
6. Рекурсивный блок. (letrec x1 = e1; x2 = e2; ... xn = en in e) :
letrec f = λn.IF (= n 0) 1 (* n (f (- n 1))) in (f 5)
Таким образом, в чистое лямбда-исчисление введены константы, функции, связывание имен со значениями и рекурсия.
10Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
5.2. Представление функциональных программ.
Компиляция с языка Haskell в расширенное лямбда-исчисление.
1. Исключаем все конструкции, связанные с определением и контролем типов:type, data, ::, class, instance
2. Многие конструкции переводятся (почти) без изменения:константа: c c
примитивная функция: f fлямбда-выражение: (\x->e) (λx.e'), где e' – результат компиляции e
переменная: x x
применение функции: (e1 e2) (e1' e2'); e1+e2 (+ e1' e2')блоки: (e where f = e1) (let f = e1' in e')
3. Кортежи переводятся в применения примитивной функции TUPLE-n:
кортеж: (e1, e2, e3) (TUPLE-3 e1' e2' e3')4. Применение конструкторов переводится в применение примитивной функции TUPLE-n, первым аргументом которого служит номер конструктора в определении типа:
например: [3] 3 : [] (TUPLE-3 1 3 (TUPLE-1 0))5. Многие конструкции, такие как фильтры, арифметические прогрессии и т.п. переводятся в вызовы соответствующих примитивных функций.
11Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
Компиляция с языка Haskell в расширенное лямбда-исчисление.
6. Определения функций, включающие образцы и условия, сначала переводятся в лямбда-выражения, содержащие конструкцию case в определяющем выражении.
например: assoc x ((y,e):lst) | x == y = e| otherwise = assoc x lst
assoc x [] = []
переходит в: assoc = \a1 -> \a2 -> case (a1, a2) of (x, ((y,e):lst)) | x == y -> e | otherwise -> assoc x lst (_, []) -> []
Конструкция case затем транслируется в расширенное лямбда-исчисление (далее).
7. Программный модуль (серия определений) транслируется в заголовок блока letrec.
name_1 = expr_1name_2 = expr_2
...name_k = expr_k
letrec name_1 = expr_1';
name_2 = expr_2';...
name_k = expr_k' in
Перед исполнением программы вычисляемое выражение компилируется и помещается в блок letrec после in.
12Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
Компиляция case-выраженияcase param of pattern_1 | cond_11 -> expr_11 | cond_12 -> expr_12 ... pattern_2 | cond_21 -> expr_21 ... ... pattern_k | cond_k1 -> expr_k1 ...
letrec f1 = λa.IF (образец_1) let (связывание_1) in IF cond_11' expr_11' IF cond_12' expr_12' ... (f2 a) (f2 a) f2 = λa.IF (образец_2) let (связывание_2) in IF cond_21' expr_21' IF cond_22' expr_22' ... (f3 a) (f3 a) ... fk = λa.IF (образец_k) ... error errorin f1 param'
13Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
Компиляция сопоставления с образцом и связыванияcase arg@(x, y) of ([], (e:_)) -> expr
Аргумент arg, удовлетворяющий образцу, должен иметь вид:
( [], ( (:) e _ ))(TUPLE-2 (TUPLE-1 0) (TUPLE-3 1 e foo))
(AND (EQ (INDEX 1 (INDEX 1 arg)) 0) (EQ (INDEX 1 (INDEX 2 arg)) 1))arg[1,1] == 0 && arg[2,1] == 1
Возможно также сопоставление с константой:
case arg of [25] -> expr
((:) 25 [])arg[1] == 1 && arg[2] == 25 && arg[3,1] == 0
Связывание:
x = arg[1]; y = arg[2]; e = arg[2,2]
14Кубенский А.А. Функциональное программирование.Глава 5. Системы исполнения функциональных программ.
Представление программ расширенного лямбда-исчисленияdata Expr = Integral Integer | Logical Bool -- константы
| Function String -- примитивные функции| Variable String -- переменные| Lambda String Expr -- лямбда-выражение| Application Expr Expr -- применение функции| Let String Expr Expr -- простой блок| Letrec [(String, Expr)] Expr -- рекурсивный блок
например: let plus = (λx.λy.x+y) in (plus 1 2)будет представлено в виде:
(Let "plus" (Lambda "x" (Lambda "y" (Application (Application (Function "+") (Variable "x")) (Variable "y")))) (Application (Application (Variable "plus") (Integral 1)) (Integral 2)))