Haskell Language
Faltbar
Suche…
Einführung
Foldable
ist die Klasse der Typen t :: * -> *
die einen Faltvorgang zulassen. Eine Falte fasst die Elemente einer Struktur in einer genau definierten Reihenfolge zusammen und verwendet dabei eine Kombinationsfunktion.
Bemerkungen
Wenn t
Foldable
, bedeutet dies, dass wir für jeden Wert ta
wissen, wie auf alle Elemente von a
von "inside" von ta
in einer festen linearen Reihenfolge zugegriffen werden kann. Dies ist die Bedeutung von foldMap :: Monoid m => (a -> m) -> (ta -> m)
: Wir "besuchen" jedes Element mit einer Zusammenfassungsfunktion und zerstören alle Zusammenfassungen. Monoid
(ist aber für verschiedene Gruppierungen unveränderlich).
Zählen der Elemente einer faltbaren Struktur
length
zählt die Vorkommen von Elementen a
in einer faltbaren Struktur ta
.
ghci> length [7, 2, 9] -- t ~ []
3
ghci> length (Right 'a') -- t ~ Either e
1 -- 'Either e a' may contain zero or one 'a'
ghci> length (Left "foo") -- t ~ Either String
0
ghci> length (3, True) -- t ~ (,) Int
1 -- '(c, a)' always contains exactly one 'a'
length
ist definiert als äquivalent zu:
class Foldable t where
-- ...
length :: t a -> Int
length = foldl' (\c _ -> c+1) 0
Beachten Sie, dass dieser Rückgabetyp Int
die Operationen beschränkt, die für Werte ausgeführt werden können, die durch Aufrufe der length
werden. fromIntegral
ist eine nützliche Funktion, die es uns ermöglicht, mit diesem Problem umzugehen.
Eine Struktur in umgekehrter Richtung falten
Jede Falte kann mit Hilfe des Dual
Monoids in die entgegengesetzte Richtung ausgeführt werden, wodurch ein vorhandenes Monoid umgedreht wird, sodass die Aggregation rückwärts geht.
newtype Dual a = Dual { getDual :: a }
instance Monoid m => Monoid (Dual m) where
mempty = Dual mempty
(Dual x) `mappend` (Dual y) = Dual (y `mappend` x)
Wenn das darunter liegende Monoid eines foldMap
Aufrufs mit Dual
umgedreht wird, läuft die Falte rückwärts; Der folgende Reverse
Typ ist in Data.Functor.Reverse
definiert:
newtype Reverse t a = Reverse { getReverse :: t a }
instance Foldable t => Foldable (Reverse t) where
foldMap f = getDual . foldMap (Dual . f) . getReverse
Wir können diese Maschinerie verwenden, um eine kurze reverse
für Listen zu schreiben:
reverse :: [a] -> [a]
reverse = toList . Reverse
Eine Instanz von Foldable für einen Binärbaum
Instanziieren Foldable
müssen Sie für mindestens eine Definition liefern foldMap
oder foldr
.
data Tree a = Leaf
| Node (Tree a) a (Tree a)
instance Foldable Tree where
foldMap f Leaf = mempty
foldMap f (Node l x r) = foldMap f l `mappend` f x `mappend` foldMap f r
foldr f acc Leaf = acc
foldr f acc (Node l x r) = foldr f (f x (foldr f acc r)) l
Diese Implementierung führt eine In-Reihenfolge-Durchquerung des Baums durch.
ghci> let myTree = Node (Node Leaf 'a' Leaf) 'b' (Node Leaf 'c' Leaf)
-- +--'b'--+
-- | |
-- +-'a'-+ +-'c'-+
-- | | | |
-- * * * *
ghci> toList myTree
"abc"
Die DeriveFoldable
Erweiterung ermöglicht GHC das Generieren von Foldable
Instanzen basierend auf der Struktur des Typs. Wir können die Reihenfolge der Maschine geschriebenen Traversal variieren durch das Layout der Einstellung Node
- Konstruktor.
data Inorder a = ILeaf
| INode (Inorder a) a (Inorder a) -- as before
deriving Foldable
data Preorder a = PrLeaf
| PrNode a (Preorder a) (Preorder a)
deriving Foldable
data Postorder a = PoLeaf
| PoNode (Postorder a) (Postorder a) a
deriving Foldable
-- injections from the earlier Tree type
inorder :: Tree a -> Inorder a
inorder Leaf = ILeaf
inorder (Node l x r) = INode (inorder l) x (inorder r)
preorder :: Tree a -> Preorder a
preorder Leaf = PrLeaf
preorder (Node l x r) = PrNode x (preorder l) (preorder r)
postorder :: Tree a -> Postorder a
postorder Leaf = PoLeaf
postorder (Node l x r) = PoNode (postorder l) (postorder r) x
ghci> toList (inorder myTree)
"abc"
ghci> toList (preorder myTree)
"bac"
ghci> toList (postorder myTree)
"acb"
Reduzieren einer faltbaren Struktur in eine Liste
toList
flacht eine Foldable
Struktur ta
in eine Liste von a
s.
ghci> toList [7, 2, 9] -- t ~ []
[7, 2, 9]
ghci> toList (Right 'a') -- t ~ Either e
"a"
ghci> toList (Left "foo") -- t ~ Either String
[]
ghci> toList (3, True) -- t ~ (,) Int
[True]
toList
ist definiert als äquivalent zu:
class Foldable t where
-- ...
toList :: t a -> [a]
toList = foldr (:) []
Durchführen eines Nebeneffekts für jedes Element einer faltbaren Struktur
traverse_
führt für jedes Element in einer Foldable
Struktur eine Applicative
Aktion aus. Es ignoriert das Ergebnis der Aktion und behält nur die Nebenwirkungen bei. (Für eine Version, die Ergebnisse nicht verwirft, verwenden Sie Traversable
.)
-- using the Writer applicative functor (and the Sum monoid)
ghci> runWriter $ traverse_ (\x -> tell (Sum x)) [1,2,3]
((),Sum {getSum = 6})
-- using the IO applicative functor
ghci> traverse_ putStrLn (Right "traversing")
traversing
ghci> traverse_ putStrLn (Left False)
-- nothing printed
for_
ist traverse_
mit umgedrehten Argumenten. Es ähnelt einer foreach
Schleife in einer imperativen Sprache.
ghci> let greetings = ["Hello", "Bonjour", "Hola"]
ghci> :{
ghci| for_ greetings $ \greeting -> do
ghci| print (greeting ++ " Stack Overflow!")
ghci| :}
"Hello Stack Overflow!"
"Bonjour Stack Overflow!"
"Hola Stack Overflow!"
sequenceA_
fasst eine Foldable
Applicative
Foldable
Aktionen zu einer einzigen Aktion zusammen und ignoriert das Ergebnis.
ghci> let actions = [putStrLn "one", putStLn "two"]
ghci> sequenceA_ actions
one
two
traverse_
ist definiert als äquivalent zu:
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr (\x action -> f x *> action) (pure ())
sequenceA_
ist definiert als:
sequenceA_ :: (Foldable t, Applicative f) -> t (f a) -> f ()
sequenceA_ = traverse_ id
Wenn Foldable
auch ein Functor
, haben traverse_
und sequenceA_
die folgende Beziehung:
traverse_ f = sequenceA_ . fmap f
Eine faltbare Struktur in ein Monoid glätten
foldMap
ordnet jedes Element der faltbaren Struktur einem Monoid
und kombiniert sie dann zu einem einzigen Wert.
foldMap
und foldr
können in Bezug aufeinander definiert werden, was bedeutet, dass Instanzen von Foldable
nur für eine von ihnen eine Definition Foldable
müssen.
class Foldable t where
foldMap :: Monoid m => (a -> m) -> t a -> m
foldMap f = foldr (mappend . f) mempty
Anwendungsbeispiel mit dem Product
:
product :: (Num n, Foldable t) => t n -> n
product = getProduct . foldMap Product
Definition von faltbar
class Foldable t where
{-# MINIMAL foldMap | foldr #-}
foldMap :: Monoid m => (a -> m) -> t a -> m
foldMap f = foldr (mappend . f) mempty
foldr :: (a -> b -> b) -> b -> t a -> b
foldr f z t = appEndo (foldMap (Endo #. f) t) z
-- and a number of optional methods
Foldable
Strukturen sind intuitiv (wenn auch nicht technisch) Container von Elementen a
die den Zugriff auf ihre Elemente in einer definierten Reihenfolge ermöglichen. Die foldMap
Operation ordnet jedes Element des Containers einem Monoid
und foldMap
sie mithilfe der Monoid
Struktur.
Prüfen, ob eine faltbare Struktur leer ist
null
zurückkehrt True
, wenn es keine Elemente a
in einer faltbaren Struktur ta
und False
, wenn ein oder mehrere sind. Strukturen, für die null
True
haben eine length
von 0.
ghci> null []
True
ghci> null [14, 29]
False
ghci> null Nothing
True
ghci> null (Right 'a')
False
ghci> null ('x', 3)
False
null
ist definiert als äquivalent zu:
class Foldable t where
-- ...
null :: t a -> Bool
null = foldr (\_ _ -> False) True