6

По нестойкой складке я имею в виду гипотетическую операцию примитивного сгиба для ассоциативных операторов, которая не гарантирует никакого упорядочения. То есть (fold + 0 [a b c d]) может быть (+ (+ a b) (+ c d)) или (+ (+ (+ a b) c) d).Возможно ли реализовать foldl/foldr, используя нечеткую складку?

Учитывая, что эта операция является сплавируемой, высокопараллелизуемой и универсальной, я думал, что она включает вместе с map и concat в качестве единственных примитивов списка для моего нерекурсивного минималистического языка. Мне удалось реализовать с ним большинство функций списка, но не сторонние складки foldl/foldr. Является ли это возможным?

+0

Как насчет [hoogling для 'fold'] (http://www.haskell.org/hoogle/?hoogle=fold)? – leftaroundabout

+1

, чтобы перефразировать полученный 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] ', если вы разрешаете гетерогенные массивы. –

+0

@WillNess uh huh, интересно! Интересно, что некоторые из моих целей компиляции не поддерживают функции высокого порядка (такие как C). Так что, в некотором смысле, 'fold' недостаточно, чтобы закончить мой язык. Я должен был отказаться от него в пользу 'foldl' как одного из немногих примитивов. Поэтому теперь мне нужны гарантии ассоциативности, чтобы сделать ее параллельной. Тем не менее, я многому учусь и получаю много удовольствия! – MaiaVictor

ответ

7

Если у вас есть fold и map, что является универсальным. Лозунг здесь foldr is made of monoids На самом деле, стандарт Haskell реализует класс типов Foldablefoldr и foldlin 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.

+0

Hmm pardon Мне нравится ваш ответ, и кажется, что вы действительно знаете, о чем говорите, но как точно я могу реализовать foldr с фолдом и картой? Я знаю почти что-нибудь о моноидах - пожалуйста, визуализируйте этот игрушечный язык, который в значительной степени содержит лямбда-исчисление + числа + массивы с этими тремя операциями и без рекурсии. Это можно сделать? – MaiaVictor

+0

Просто пример, иллюстрирующий: 'map = (λ (λ (mapreduce (λ (список ((2 0)))) conc nil 0))); zipWith = (λ (λ (λ (отображение (λ (3 (get 0 3) (get 0 1))) (диапазон 0 (len 1)))))) '. Эти числа являются индексами bruijn для переменных, имейте это в виду. Я реализовал множество функций аналогично, используя только 'mapreduce',' list', 'conc',' nil'. Но склад ... Я не знаю. Я не знаю, как имитировать аспект последовательности, учитывая, что 'mapreduce' (нечеткая складка) не гарантирует порядок работы. Как? – MaiaVictor

+1

@Viclib Как уже упоминалось в ответе, просто посмотрите на источник для 'Foldable', чтобы увидеть реализации по умолчанию' foldr' и 'foldl' в терминах' foldMap'. –

3
foldr f z xs = fold (.) id (map f xs) z 

Например, в GHCI:

*Dmwit Debug.SimpleReflect> let foldr' f z xs = foldb (.) id (map f xs) z 
*Dmwit Debug.SimpleReflect> foldr' f z [w,x,y] 
f w (f x (f y z)) 
*Dmwit Debug.SimpleReflect> foldr f z [w,x,y] 
f w (f x (f y z)) 
+0

Спасибо вам большое! Я отмечаю Филиппа, когда он объяснил ему подробности после того, как вы опубликовали их, но это очень помогло. – MaiaVictor

 Смежные вопросы

  • Нет связанных вопросов^_^