Естественно, это зависит именно от того, как вы реализовали кортежи, списки и т. Д. В Lambda Calculus. Но с учетом стандартных функциональных списков cons
и того, что оба списка имеют одинаковую длину, и что вы уже определили следующих помощников, некоторые из которых вы уже цитировали:
cons -- construct a list from a node and another list
empty -- the empty list
head -- retrieve the first node value of a list
tail -- retrieve all but the first node of a list
pair -- pair values into a tuple
isEmpty -- return `true` if a list is `empty`, `false` otherwise
true -- return the first argument
false -- return the second argument
Тогда мы можем рекурсивно сжать списки следующим образом:
ZIP = λlk. isEmpty l -- "if the list is empty…"
empty -- "return an empty result. Else…"
(cons -- "return a new list, containing…"
(pair (head l) (head k)) -- "the zipped heads, and…"
(ZIP (tail l) (tail k))) -- "everything else zipped."
Проблема, конечно, заключается в том, что исходное лямбда-исчисление не имеет рекурсии . Вы не можете ссылаться на имя функции в ее собственном определении. Ответ заключается в использовании комбинатора с фиксированной точкой, например, знаменитый Y
комбинатор .
ZIP = Y (λzlk. isEmpty l empty (cons (pair (head l) (head k)) (z (tail l) (tail k)))
Определение Y
:
Y = λf.(λx.f(x x))(λx.f(x x))
Распутывание того, как именно это работает - впечатляющая часть умственной гимнастики, которая не совсем подходит для этого вопроса, но с использованием довольно проста. В общем, если у вас есть желаемое (но недопустимое, в необработанном LC) рекурсивное определение, подобное этому:
R = λ ??? . ??? R ???
Вместо этого вы можете написать это как абсолютно легальное, нерекурсивное определение:
R = Y (λr ??? . ??? r ???)
Другими словами, добавьте новый параметр в вашу функцию, который означает для вашей рекурсивной функции, и используйте Y
, чтобы связать все для вас. То, что Y
может изобрести рекурсию с нуля, необычайно, и причина, по которой он так знаменит.