Ну, мы можем прокомментировать тип церковного списка таким образом, чтобы уточнить это:
-- Tell me...
type Churchlist t u = (t -> u -> u) -- ...how to handle a pair
-> u -- ...and how to handle an empty list
-> u -- ...and then I'll transform a list into
-- the type you want
Обратите внимание, что это тесно связано с функцией foldr
:
foldr :: (t -> u -> u) -> u -> [t] -> u
foldr k z [] = z
foldr k z (x:xs) = k x (foldr k z xs)
foldr
- это очень общая функция, которая может реализовывать все виды других функций списка. Простой пример, который поможет вам реализовать копию списка с помощью foldr
:
copyList :: [t] -> [t]
copyList xs = foldr (:) [] xs
Используя приведенный выше тип комментария, foldr (:) []
означает следующее: «если вы видите пустой список, верните пустой список, и если вы видите пару, верните head:tailResult
.»
Используя Churchlist
, вы легко можете написать аналог:
-- Note that the definitions of nil and cons mirror the two foldr equations!
nil :: Churchlist t u
nil = \k z -> z
cons :: t -> Churchlist t u -> Churchlist t u
cons x xs = \k z -> k x (xs k z)
copyChurchlist :: ChurchList t u -> Churchlist t u
copyChurchlist xs = xs cons nil
Теперь, чтобы реализовать map
, вам просто нужно заменить cons
подходящей функцией, например:
map :: (a -> b) -> [a] -> [b]
map f xs = foldr (\x xs' -> f x:xs') [] xs
Отображение похоже на копирование списка, за исключением того, что вместо простого дословного копирования элементов вы применяете f
к каждому из них.
Внимательно изучите все это, и вы сможете написать mapChurchlist :: (t -> t') -> Churchlist t u -> Churchlist t' u
самостоятельно.
Дополнительное упражнение (простое): запишите эти функции списка в терминах foldr
и напишите аналоги для Churchlist
:
filter :: (a -> Bool) -> [a] -> [a]
append :: [a] -> [a] -> [a]
-- Return first element of list that satisfies predicate, or Nothing
find :: (a -> Bool) -> [a] -> Maybe a
Если вам хочется заняться чем-то сложнее, попробуйте написать tail
для Churchlist
. (Начните с записи tail
для [a]
, используя foldr
.)