
5.2. Программирование на языке лямбда-исчисления 59
omega = (λx . x x ) (λx . x x );
содержит только один редекс, но шаг вычисления этого редекса дает в результате опять omega! Про
термы, не имеющие нормальной формы, говорят, что они не завершаются.
Комбинатор omega можно обобщить до полезного терма, называемого комбинатор неподвижной
точки,
6
и с его помощью можно определять рекурсивные функции, например, factorial.
7
fix = λf . (λx. f (λy. x x y )) (λx . f (λy . x x y ));
Подобно omega, комбинатор fix имеет сложную структуру с повторами; глядя на определение,
трудно понять, как он работает. Вероятно, лучший способ заработать интуицию о его поведении — рас-
смотреть его действие в конкретном примере.
8
Допустим, мы хотим написать рекурсивное определение
функции вида h = тело, содержащее h — т. е., нам хочется написать определение, в котором пра-
вая часть использует ту самую функцию, которую мы определяем, как в определении факториала на
странице 50. Идея в том, чтобы рекурсивное определение «разворачивалось» там, где оно встретится;
скажем, определение факториала, интуитивно,
if n =0 then 1
else n * ( if n -1=0 then 1
else (n -1) * ( if n -2=0 then 1
else (n -2) * \ ldots ))
или, в терминах чисел Чёрча,
if real eq n c
0
then c
1
else tim es n (if re aleq ( prd n) c
0
then c
1
else tim es ( prd n)
( if rea leq ( prd ( prd n )) c
0
then c
1
else tim es ( prd ( prd n )) \ ldots ))
Этого можно добиться при помощи комбинатора fix, сначала определив g = тело, содержащее f , а
затем h = fix g. Например, функцию факториала можно определить через
g = λfct . λn . if rea leq n c
0
then c
1
else ( times n ( fct ( prd n )));
fac t o rial = fix g;
На Рис. 5.2 показано, что происходит с термом factorial c
3
при вычислении. Ключевое свойство,
которое обеспечивает работу этого вычисления, это fct n g fct n. Таким образом, fct — своего
рода «самовоспроизводящийся автомат», который, будучи применен к аргументу n, дает самого себя
и n в качестве аргументов g. Там, где первый аргумент встречается в теле g, мы получим еще одну
копию fct, которая, будучи применена к аргументу, опять передаст самое себя и аргумент внутрь g, и
т. д. Каждый раз, когда мы делаем рекурсивный вызов при помощи fct, мы разворачиваем очередную
копию g и снабжаем ее очередными копиями fct, готовыми развернуться еще дальше.
Упражнение 5.2.9 : Почему в определении g мы использовали форму if, а не функцию test, рабо-
тающую с Чёрчевыми булевскими значениями? Покажите, как определить функцию factorial при
помощи test вместо if.
Упражнение 5.2.10 : Напишите функцию churchnat, переводящую элементарное натуральное
число в представление Чёрча.
Упражнение 5.2.11 Рекомендуется, : С помощью fix и кодирования списков из Упражне-
ния 5.2.8 напишите функцию, суммирующую список, состоящий из чисел Чёрча.
6
Часто его называют Y -комбинатор с вызовом по значению. Плоткин (Plotkin 1977) использовал обозначение Z.
7
Заметим, что более простой комбинатор неподвижной точки с вызовом по имени
Y = λf. (λx . f ( x x )) (λx . f ( x x ))
в условиях вызова по значению бесполезен, поскольку при любом g выражение Y g не завершается.
8
Возможно также вывести определение fix, исходя из базовых принципов (см., например, Friedman and Felleisen 1996,
глава 9), однако сам такой вывод достаточно хитроумен.
rev. 104