Ricerca…


introduzione

Foldable è la classe di tipi t :: * -> * che ammette un'operazione di piegatura . Una piega aggrega gli elementi di una struttura in un ordine ben definito, utilizzando una funzione di combinazione.

Osservazioni

Se t è Foldable significa che per qualsiasi valore ta sappiamo come accedere a tutti gli elementi di a da "dentro" di ta in un ordine lineare fisso. Questo è il significato di foldMap :: Monoid m => (a -> m) -> (ta -> m) : noi "visitiamo" ogni elemento con una funzione di riepilogo e distruggiamo tutti i sommari insieme. Monoid ordine di rispetto dei Monoid (ma è invariante rispetto ai diversi raggruppamenti).

Conteggio degli elementi di una struttura pieghevole

length conta le occorrenze degli elementi a in una struttura pieghevole 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 è definita come equivalente a:

class Foldable t where
    -- ...
    length :: t a -> Int
    length = foldl' (\c _ -> c+1) 0

Si noti che questo tipo di restituzione Int limita le operazioni che possono essere eseguite sui valori ottenuti dalle chiamate alla funzione di length . fromIntegral è una funzione utile che ci consente di affrontare questo problema.

Piegare una struttura al contrario

Qualsiasi piega può essere eseguita nella direzione opposta con l'aiuto del Dual monoid , che ribalta un monoide esistente in modo che l'aggregazione vada all'indietro.

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)

Quando il monoide sottostante di una chiamata foldMap viene capovolto con Dual , la piega viene eseguita all'indietro; il seguente tipo di Reverse è definito in 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

Possiamo usare questo macchinario per scrivere un reverse per le liste:

reverse :: [a] -> [a]
reverse = toList . Reverse

Un'istanza di Pieghevole per un albero binario

Per istanziare Foldable è necessario fornire una definizione di almeno foldMap o 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

Questa implementazione esegue una traversata in ordine dell'albero.

ghci> let myTree = Node (Node Leaf 'a' Leaf) 'b' (Node Leaf 'c' Leaf)

--    +--'b'--+
--    |       |
-- +-'a'-+ +-'c'-+
-- |     | |     |
-- *     * *     *

ghci> toList myTree
"abc"

L'estensione DeriveFoldable consente a GHC di generare istanze Foldable base alla struttura del tipo. Possiamo variare l'ordine di attraversamento scritto da macchina modificando il layout del costruttore Node .

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"

Appiattimento di una struttura pieghevole in un elenco

toList appiattisce un Foldable struttura ta in un elenco di 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 è definito come equivalente a:

class Foldable t where
    -- ...
    toList :: t a -> [a]
    toList = foldr (:) []

Esecuzione di un effetto collaterale per ciascun elemento di una struttura pieghevole

traverse_ esegue un'azione Applicative per ogni elemento in una struttura Foldable . Ignora il risultato dell'azione, mantenendo solo gli effetti collaterali. (Per una versione che non scarta risultati, usa 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_ is traverse_ con gli argomenti capovolti. Assomiglia a un ciclo di foreach in un linguaggio imperativo.

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_ collassa un Foldable pieno di azioni Applicative in una singola azione, ignorando il risultato.

ghci> let actions = [putStrLn "one", putStLn "two"]
ghci> sequenceA_ actions
one
two

traverse_ è definito come equivalente a:

traverse_ :: (Foldable t, Applicative f) => (a -> f b) -> t a -> f ()
traverse_ f = foldr (\x action -> f x *> action) (pure ())

sequenceA_ è definito come:

sequenceA_ :: (Foldable t, Applicative f) -> t (f a) -> f ()
sequenceA_ = traverse_ id

Inoltre, quando il Foldable è anche un Functor , traverse_ e sequenceA_ avere la seguente relazione:

traverse_ f = sequenceA_ . fmap f

Appiattimento di una struttura pieghevole in un mono

foldMap mappa ogni elemento della struttura Pieghevole in un Monoid e quindi li combina in un singolo valore.

foldMap e foldr possono essere definite in termini di uno dall'altro, il che significa che i casi di Foldable bisogno solo dare una definizione di uno di loro.

class Foldable t where
    foldMap :: Monoid m => (a -> m) -> t a -> m
    foldMap f = foldr (mappend . f) mempty

Esempio di utilizzo con il Product monoid :

product :: (Num n, Foldable t) => t n -> n
product = getProduct . foldMap Product

Definizione di pieghevole

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

Intuitivamente (anche se non tecnicamente), Foldable strutture sono contenitori di elementi a che permettono l'accesso ai loro elementi in un ordine ben definito. L'operazione foldMap mappa ogni elemento del contenitore su un Monoid e li comprime usando la struttura Monoid .

Verifica se una struttura pieghevole è vuota

null restituisce True se non ci sono elementi a in una struttura pieghevole ta e False se ce n'è uno o più. Le strutture per le quali null è True hanno una length pari a 0.

ghci> null []
True
ghci> null [14, 29]
False
ghci> null Nothing
True
ghci> null (Right 'a')
False
ghci> null ('x', 3)
False

null è definito come equivalente a:

class Foldable t where
    -- ...
    null :: t a -> Bool
    null = foldr (\_ _ -> False) True


Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow