Лекции / 8
.pdf§ Базовые программные конструкции в нотации лямбда-исчисления.
Логические значения true и false определяются следующим образом:
TRUE = λxy.x (функция, всегда возвращающая первый аргумент)
FALSE = λxy.y (функция, всегда возвращающая второй аргумент)
Как известно, условный оператор IF имеет следующий вид: IF c THEN a ELSE b
Ему соответствует функция с 3 аргументами: IF (c, a, b)
λ-выражение конструкции IF с 3 аргументами согласно определению абстракции: IF = λc.λa.λb.X = λcab.X
Обратим внимание, что это запись соответствует тому, что абстракция интерпретируется ассоциативно вправо, то есть:
λcab.X означает (λc.(λa.(λb.(X))))
Напишем cab вместо X, то есть IF = λcab.cab.
Тогда, если c = true, то есть: IF true THEN a ELSE b
то это в функциональном смысле эквивалентно записи (true a b)
или (подставляя TRUE = λxy.x) λxy.x a b
или
a
(функция вернула первый аргумент)
Если c = false, то есть:
IF false THEN a ELSE b
то это в функциональном смысле эквивалентно записи (false a b)
или
λxy.y a b
или
b
(функция вернула второй аргумент)
1
Теперь запишем эти преобразования в форме редукций.
Итак, IF = λcab.cab и в это лямбда-выражение вместо c подставляем true, то есть λxy.x или, что то же самое согласно определению альфа-редукции (замене связанных переменных), λab.a. Получаем:
IF TRUE → λab.(λab.a)ab → λab.a
IF FALSE → λab.(λab.b)ab → λab.b
Пример реализации
c = FALSE
IF c 1 0 = (λcab.cab) (λxy.y) 1 0
Первая подстановка приводит к выражению (λab.(λxy.y)ab) 1 0
Далее есть два варианта подстановки:
Первый вариант:
[x/a], [y/b] в λxy.y: → (λab.b) 1 0 → 0
Второй вариант:
[a/1], [b/0]: → (λxy.y) 1 0 → 0
Реализация других логических операций (отрицания, конъюнкции, дизъюнкции):
NOT = λcab.cba
Пример: NOT TRUE = (λcab.cba) TRUE = (λab.(λxy.x) ba) = (λab.b) = FALSE
AND = λab.aba
OR = λab.aab
not p = if p then false else true p and q = if p then q else false p or q = if p then true else q
Проведем аналогию лямбда-исчисления и языка Haskell. Функции true, false, if можно записать следующим образом:
true a b = a false a b = b if c a b = c a b
Определения можно ввести и по-другому. В языке Haskell есть встроенный тип Bool:
data Bool = True | False
if :: Bool -> a -> a -> a if True t e = t
if False t e = e
В процедурных языках для преобразования выражений используется эквивалент аппликативной редукционной стратегии. В языке Haskell используется аналог нормальной стратегии.
Пары, кортежи, списки
Пара – упорядоченный набор двух значений, кортеж – упорядоченный набор n элементов.
struct pair {TYPE_1 first; TYPE_2 second;};
2
Введем выражения
first p = λp.p true second p = λp.p false
Тогда выражение
pair = λb.b p q или λxyf.fxy
создает пару (x,y), функция first возвращает первый элемент кортежа, second — второй.
first (p, q) = (p, q) true = (λb.b p q) true = true p q = (λxy. x) p q = p second (p, q) = (p, q) false = (λb.b p q) false = false p q = (λxy.y) p q = q
second (pair 4 5) → second ((λxyf.fxy) 4 5) → second ((λyf.f 4 y) 5) → second (λf.f 4 5) → (λp.p false) (λf.f 4 5) → (λf.f 4 5) false → false 4 5 → (λxy.y) 4 5 → 5
Аналогично можно определять другие структуры, скажем, IP-адрес:
IP = λabcdf.fabcd
IP_OCTET_A = λp.p (λabcd.a)
IP_OCTET_B = λp.p (λabcd.b)
IP_OCTET_C = λp.p (λabcd.c)
IP_OCTET_D = λp.p (λabcd.d)
Применение выражения pair к паре позволяет создавать кортежи из трёх и большего количества элементов, например pair x (pair y z) — кортеж из трёх элементов.
(p, q, r, s) = (p, (q, (r, s))) = λb.b p (q, (r, s)) = λb.b p (λb.b q (r, s)) = λb.b p (λb.b q (λb.b r s)) = λb.b p (λg.g q (λh.h r s))
С помощью лямбда-исчисления также можно строить связные списки. Каждый элемент списка должен содержать как минимум одно значение и ссылку на следующий элемент, то есть пару.
Для разграничения пар используем дополнительные пары с первым элементом, равным FALSE:
Создание пары: λxy.( pair false (pair x y))
Извлечение первого элемента пары: λx.(first (second x)) Извлечение второго элемента пары: λx.( second (second x)) Пустой список: NIL = λx.x
Проверка на пустой список: ISNULL = first
Аналоги всех введенных операций существуют в языке Lisp.
Числа в нумерации Черча.
В нумерации Черча числа выражаются функциями двух аргументов:
0 = FALSE = λfx.x
1 = λfx.fx
2 = λfx.f (fx)
3 = λfx.f ( f (fx))
…
n = λfx.f(...<n раз>...(fx)...) λfx. f n x,
и называются нумералами Черча.
Эти выражения фактически представляют собой запись чисел в системе счисления по основанию 1: 1, 11, 111, 1111, 11111, 111111, . . ..
Увеличение нумерала Черча на единицу: λnfx.n f (f x)
3
(λnfx.n f (f x)) (λfx.f n x) = λfx.(λfx.f n x) f (f x) = λfx.(λx.fn x)(f x) = λfx.fn (f x) = λfx.fn+1 x = n + 1 Проверка n на равенство нулю: λn.n (λx. false) true
Если ноль: (λn.n (λx.FALSE) TRUE) 0 = 0 (λx.FALSE) TRUE = λfx.x (λx.FALSE) TRUE = TRUE
Если не ноль: (λn.n (λx.FALSE) TRUE) 1 = 1 (λx.FALSE) TRUE = λfx.fx (λx.FALSE) TRUE = (λx.FALSE) TRUE = (λx.λab.b) TRUE = λab.b = FALSE
Сложение нумералов Черча: m+n = λfx.m f (n f x)
Произведение нумералов Черча: m n = λf x. m (n f) x
4