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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow