2017-02-27 20 views
9

Próbowałem dowiedzieć się, jak zrobić coś wzdłuż liniiCzy to możliwe, aby zrobić listę funkcji i powrócić do funkcji, która jest ich kompozytowy

compositeFunctions :: [(a -> a)] -> (a -> a) 

myślałem mogłem użyć foldr stale spasuj listę funkcji, ale nie mogę niczego wymyślić.

+7

Jesteś na dobrej drodze! rozważ 'foldr :: (x -> y -> x) -> x -> [y] -> x', teraz zamień' a -> a' na 'y'. Czym musi być 'x'? Jaka jest dobra domyślna wartość parametru typu "x"? Jaka powinna być wartość typu 'x -> y -> x'? – rampion

+1

'foldMap' (jak w' compositeFunctions = appEndo. FoldMap Endo') przekazuje _intent_ lepiej niż 'foldr', jak sądzę. (I jest tylko garstką znaków dłuższych niż 'foldr (.) Id') – Alec

+0

Używanie' foldMap' jest bardzo niejasne, imo. – augustss

Odpowiedz

11

foldr robi dokładnie to, co chcesz, ponieważ id jest tożsamość dla (.) (tj f . id == f):

compose :: [(a -> a)] -> a -> a 
compose = foldr (.) id 

W bardziej zrozumiałej formie rekurencyjnej:

compose' [] = id 
compose' (f:fs) = f . compose' fs 
8

funkcje postaci a -> a , czyli argument i wynik mają ten sam typ, są znane jako endomorfizmy endomorfizmów. Naprawdę fajną rzeczą o endomorfizmach dla danego a jest to, że tworzą one monoid z id jako tożsamością i (.) jako operator. Oznacza to, że mconcat powinni robić dokładnie to, co chcesz ...

compositeFunctions = mconcat 

... niestety jest to nieco bardziej skomplikowane. W celu uzyskania na przykład Monoid, będziesz musiał owinąć swoje funkcje w Endo newtype z Data.Monoid a następnie rozpakować Rezultat:

compositeFunctions = appEndo . mconcat . fmap Endo 
+4

Lub bardziej zwięźle "compositeFunctions = appEndo. foldMap Endo', o czym wspomniałem w swoim komentarzu. – Alec

+0

Można również użyć pakietu reduktorów, staje się to jak 'reduceWith appEndo'. – mb14

+1

Dlaczego '(a -> a)' nie ma samej instancji Monoid? Dlaczego jest potrzebny nowy typ? – amalloy