Хорошо, мне удалось написать решение для этого. Прежде всего давайте определим тип помощника option
:
option = forall 'a, 'x. ('a -> 'x) -> 'x -> 'x
Который имеет два конструктора:
none = Λ'a . Λ'x . λ (onsome : 'a -> 'x) . λ (onnone : 'x) . onnone
some = Λ'a . Λ'x . λ (elem : 'a) . λ (onsome : 'a -> 'x) . λ (onnone : 'x) . onsome elem
Следующим шагом являются функции, которые извлекут head
и tail
из списка. head
вернет option 'elemtype
для обработки пустого списка, но tail
просто вернет пустой список в пустом списке (аналогично функции-предшественнику для церковных чисел)
head = Λ 'a . λ (l : list 'a) . l (λ (elem : 'a) . λ (p : option 'a) . some elem) none
tail = Λ 'a . λ (l : list 'a) .
pair_first
( l (λ (elem : 'a) . λ (p : pair (list 'a) (list 'a)) .
make_pair (list 'a) (list 'a)
(pair_second (list 'a) (list 'a) p)
(cons 'a elem (pair_second (list 'a) (list 'a) p)))
(make_pair (list 'a) (list 'a) (nil 'a) (nil 'a)))
Идея head
состоит в том, что мы начинаем агрегирование нашего списка, начиная с none
в пустом списке, и для каждого нового элемента слева мы устанавливаем этот элемент как наш результат, обернутый some
, чтобы сохранить типизацию.
tail
с другой стороны, не нужно option
, чтобы быть четко определенным, потому что в случае пустого списка мы можем просто вернуть пустой список. Это выглядит очень некрасиво, но использует ту же технику, что и предшественник натуральных чисел. Я предполагаю, что pair
интерфейс известен.
Далее, давайте определим функцию listmatch
, которая будет соответствовать шаблону в данном списке
listmatch = Λ 'a . Λ 'x . λ (l : list 'a) . λ (oncons : 'a -> list 'a -> 'x) . λ (onnil : 'x) .
(head 'a l)
(λ (hd : 'a) . oncons hd (tail 'a l))
onnil
Эта функция помогает нам различать пустой список и непустой список и выполнять некоторые действия после его уничтожения.
Почти наконец, нам бы хотелось иметь функцию foldl2
с данной функцией f
, пустым регистром em
и двумя списками [a,b,c]
и [x,y]
, возвращающими что-то вроде этого: f(f(em a x) b y)
(сокращает списки до короче один отрезает хвост).
Может быть определено как
foldl2 =
Λ 'a . Λ 'b . Λ 'c .
λ (f : 'c -> 'a -> 'b -> 'c) . λ (em : 'c) . λ (la : list 'a) . λ (lb : list 'b) .
pair_first 'c (list 'b)
((reverse 'a la)
( λ (el : 'a) . λ (p : pair 'c (list 'b)) .
listmatch 'a (pair 'c (list 'b)) (pair_second 'c (list 'b) p)
(λ (hd : 'a) . λ (tl : list 'a) .
make_pair 'c (list 'b)
(f (pair_first 'c (list 'b) p) el hd)
tl)
(make_pair 'c (list 'b) (pair_first 'c (list 'b) p) (nil 'a))
)
(make_pair 'c (list 'b) em lb))
После этого zip
просто в наших руках:
zip =
Λ 'a . Λ 'b . Λ 'c .
λ (f : 'a -> 'b -> 'c) . λ (la : list 'a) . λ (lb : list 'b) .
reverse 'c
(foldl2 'a 'b 'c
(λ (lt : 'c) . λ (a : 'a) . λ (b : 'b) . cons 'c (f a b) lt)
(nil 'c) la lb)