Haskell Language
Vikbar
Sök…
Introduktion
Foldable
är klassen av typerna t :: * -> *
som medger en vikningsoperation . En vikning sammanför elementen i en struktur i en väl definierad ordning med en kombinerande funktion.
Anmärkningar
Om t
är Foldable
betyder det att för alla värden ta
vi vet hur vi får åtkomst till alla elementen i a
från "insidan" av ta
i en fast linjär ordning. Detta är innebörden av foldMap :: Monoid m => (a -> m) -> (ta -> m)
: vi "besöker" varje element med en sammanfattningsfunktion och krossar alla sammanfattningar tillsammans. Monoid
respekterar ordning (men är oberoende av olika grupper).
Räkna elementen i en hopfällbar struktur
length
räknar förekomsten av element a
i en hopfällbar 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
definieras som motsvarande:
class Foldable t where
-- ...
length :: t a -> Int
length = foldl' (\c _ -> c+1) 0
Observera att detta returtyp Int
begränsar de operationer som kan utföras på värden som erhållits genom anrop till length
-funktionen. fromIntegral
är en användbar funktion som gör att vi kan hantera detta problem.
Vik en struktur i omvänd riktning
Varje vikning kan köras i motsatt riktning med hjälp av Dual
monoiden , som vänder en befintlig monoid så att aggregeringen går bakåt.
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)
När den underliggande monoiden i ett foldMap
samtal vänds med Dual
, körs vecket bakåt; följande Reverse
typ definieras i Data.Functor.Reverse
:
newtype Reverse t a = Reverse { getReverse :: t a }
instance Foldable t => Foldable (Reverse t) where
foldMap f = getDual . foldMap (Dual . f) . getReverse
Vi kan använda den här maskinen för att skriva en reverse
lista för listor:
reverse :: [a] -> [a]
reverse = toList . Reverse
En instans av vikbar för ett binärt träd
För att omedelbar Foldable
måste du ange en definition för åtminstone foldMap
eller 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
Denna implementering utför en beställning av trädet.
ghci> let myTree = Node (Node Leaf 'a' Leaf) 'b' (Node Leaf 'c' Leaf)
-- +--'b'--+
-- | |
-- +-'a'-+ +-'c'-+
-- | | | |
-- * * * *
ghci> toList myTree
"abc"
DeriveFoldable
förlängningen gör att GHC kan generera Foldable
instanser baserade på typen av struktur. Vi kan variera ordningen på den maskinskrivna traversalen genom att justera layouten för Node
konstruktören.
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"
Att platta en hopfällbar struktur i en lista
toList
plattar en Foldable
struktur ta
i en lista över 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
definieras som motsvarande:
class Foldable t where
-- ...
toList :: t a -> [a]
toList = foldr (:) []
Utföra en bieffekt för varje element i en hopfällbar struktur
traverse_
utför en Applicative
åtgärd för varje element i en Foldable
struktur. Den ignorerar åtgärdens resultat och behåller bara biverkningarna. (För en version som inte kasserar resultat, använd 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_
är traverse_
med argumenten vänt. Det liknar en foreach
på ett nödvändigt språk.
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_
kollapsar en Foldable
full av Applicative
till en enda åtgärd och ignorerar resultatet.
ghci> let actions = [putStrLn "one", putStLn "two"]
ghci> sequenceA_ actions
one
two
traverse_
definieras som likvärdigt med:
traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr (\x action -> f x *> action) (pure ())
sequenceA_
definieras som:
sequenceA_ :: (Foldable t, Applicative f) -> t (f a) -> f ()
sequenceA_ = traverse_ id
Dessutom, när den Foldable
är också en Functor
, traverse_
och sequenceA_
har följande samband:
traverse_ f = sequenceA_ . fmap f
Att platta en hopfällbar struktur till en monoid
foldMap
kartlägger varje element i den hopfällbara strukturen till en Monoid
och kombinerar dem sedan till ett enda värde.
foldMap
och foldr
kan definieras i termer av varandra, vilket innebär att instanser av Foldable
bara behöver ge en definition för en av dem.
class Foldable t where
foldMap :: Monoid m => (a -> m) -> t a -> m
foldMap f = foldr (mappend . f) mempty
Exempel på användning med Product
:
product :: (Num n, Foldable t) => t n -> n
product = getProduct . foldMap Product
Definition av vikbar
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
Intuitivt (men inte tekniskt) är Foldable
strukturer behållare med element a
som tillåter åtkomst till deras element i en väl definierad ordning. foldMap
operationen kartlägger varje element i behållaren till en Monoid
och kollapsar dem med Monoid
strukturen.
Kontrollera om en hopfällbar struktur är tom
null
returnerar True
om det inte finns några element a
i en hopfällbar struktur ta
, och False
om det finns ett eller flera. Strukturer för vilka null
är True
har en length
på 0.
ghci> null []
True
ghci> null [14, 29]
False
ghci> null Nothing
True
ghci> null (Right 'a')
False
ghci> null ('x', 3)
False
null
definieras som likvärdigt med:
class Foldable t where
-- ...
null :: t a -> Bool
null = foldr (\_ _ -> False) True