Если у вас есть fold
и map
, что является универсальным. Лозунг здесь foldr is made of monoids На самом деле, стандарт Haskell реализует класс типов Foldable
foldr
и foldl
in just this way
Хитрость заключается в том, что множество эндоморфизмов над множеством форм Моноид под функцией композиции с функцией идентичности как личности.
Обратите внимание, что foldr
и foldl
являются неотъемлемо последовательными. Таким образом, этот трюк должен отказаться от любого параллелизма, который у вас есть при реализации fold
и map
. По сути, кодирование foldr
в foldMap
является кодированием отложенного последовательного вычисления в потенциально неупорядоченный. Вот почему я рекомендую использовать foldMap
по сравнению с foldr
, когда это возможно - он поддерживает неявный параллизм, когда это возможно, но эквивалентно выразительной мощности.
EDIT: Сложив все в одном месте
Определит множество эндо морфизмов над a
newtype Endo a = Endo { appEndo :: a -> a }
instance Monoid (Endo a) where
mempty = Endo id
Endo f `mappend` Endo g = Endo (f . g)
затем в складном, мы видим определение foldr
foldr f z t = appEndo (foldMap (Endo . f) t) z
этом используется foldMap
, который имеет тип Monoid m => (a -> m) -> t a -> m
(где t
- это коллекция, которую мы складываем ING более, мы можем делать вид, что список теперь дает Monoid m => (a -> m) -> [a] -> m
и эквивалентен
foldMap f ls = fold (map f ls)
где fold
находится в моноидный раз. Если у вас есть неупорядоченная складка под названием fold' :: (a -> a -> a) -> a -> [a] -> a
то, что это просто
fold = fold' mappend mempty
так
foldr f z t = appEndo (foldMap (Endo . f) t) z
= appEndo (fold (map (Endo . f) t)) z
= appEndo (fold' mappend mempty (map (Endo . f) t)) z
= appEndo (fold' (\(Endo f) (Endo g) -> Endo (f . g) (Endo id) (map (Endo . f) t)) z
, которые могут быть еще более упрощен до
foldr f z t = (fold' (.) id (map f t)) z
и сбросив ненужный Паренс
foldr f z t = fold' (.) id (map f t) z
, что дал Даниэль Вагнер в качестве ответа. Вы можете реализовать foldl
аналогичным образом или через foldr
.
Как насчет [hoogling для 'fold'] (http://www.haskell.org/hoogle/?hoogle=fold)? – leftaroundabout
, чтобы перефразировать полученный Haskell ответ, в Haskell это последовательное перераспределение выполняется путем задержки, складывания функциональной композиции (с присущим ей порядком применения) и окончательного применения нулевого элемента. Это переводит 'a + b' в' (a +). (b +) 'для' foldr' или '(a +) >>> (b +)' для 'foldl' (*' (fg) x = f (gx) ';' (f >>> g) x = g (fx) '*). Тогда ваш язык должен поддерживать частичное приложение. Оценка приводит к перестройке. Или вы можете подтвердить это как явные вырожденные деревья, '[a [b [c [d]]]] или' [[[[a] b] c] d] ', если вы разрешаете гетерогенные массивы. –
@WillNess uh huh, интересно! Интересно, что некоторые из моих целей компиляции не поддерживают функции высокого порядка (такие как C). Так что, в некотором смысле, 'fold' недостаточно, чтобы закончить мой язык. Я должен был отказаться от него в пользу 'foldl' как одного из немногих примитивов. Поэтому теперь мне нужны гарантии ассоциативности, чтобы сделать ее параллельной. Тем не менее, я многому учусь и получаю много удовольствия! – MaiaVictor