2013-03-23 13 views

risposta

10

DList a è un involucro intorno newtype [a] -> [a], che ha un a in posizione controvariante, quindi non si può attuare Foldable o Traversable, o anche Functor direttamente. L'unico modo per implementarli è la conversione da e verso gli elenchi normali (vedere l'implementazione foldr), che sconfigge il vantaggio prestazionale degli elenchi di differenze.

+2

seguito alla Risposta di Sjoerd, un DList è efficace solo per ** building **: se hai compilato il tuo elenco e vuoi elaborarlo, devi coprirlo con 'toList', quindi elaborare l'elenco regolare. –

+4

Quindi, perché non definiamo semplicemente 'fold (DL f) = fold (f [])'? Possiamo dimenticarci di come 'DList's sono implementati e semplicemente vederli come una rappresentazione della sequenza di elementi, e quindi implementare' Foldable' ha senso. L'implementazione di 'Functor' e' Traversable' in questo modo avrebbe probabilmente qualche insidia, ma 'Foldable' sembra abbastanza ragionevole. –

+2

Sì, 'Pieghevole' potrebbe non essere un problema, il pacchetto ha' foldr' già e dopo tutto è abbastanza. Immagino che la ragione per cui non è stata implementata è perché l'ultimo aggiornamento era nel 2009, quando 'Foldable' non era ancora una classe di caratteri ben nota. –

16

Un'alternativa da considerare invece di DList consiste nell'utilizzare elenchi codificati di Church. L'idea è di rappresentare un elenco come un valore opaco che sa come eseguire uno foldr su un elenco. Ciò richiede l'utilizzo del RankNTypes estensione:

{-# LANGUAGE RankNTypes #-} 

import Prelude 
import Control.Applicative 
import Data.Foldable (Foldable) 
import qualified Data.Foldable as F 
import Data.Traversable (Traversable) 
import qualified Data.Traversable as T 

-- | Laws: 
-- 
-- > runList xs cons nil == xs 
-- > runList (fromList xs) f z == foldr f z xs 
-- > foldr f z (toList xs) == runList xs f z 
newtype ChurchList a = 
    ChurchList { runList :: forall r. (a -> r -> r) -> r -> r } 

-- | Make a 'ChurchList' out of a regular list. 
fromList :: [a] -> ChurchList a 
fromList xs = ChurchList $ \k z -> foldr k z xs 

-- | Turn a 'ChurchList' into a regular list. 
toList :: ChurchList a -> [a] 
toList xs = runList xs (:) [] 

-- | We can construct an empty 'ChurchList' without using a @[]@. 
nil :: ChurchList a 
nil = ChurchList $ \_ z -> z 

-- | The 'ChurchList' counterpart to '(:)'. Unlike 'DList', whose 
-- implementation uses the regular list type, 'ChurchList' doesn't 
-- rely on it at all. 
cons :: a -> ChurchList a -> ChurchList a 
cons x xs = ChurchList $ \k z -> k x (runList xs k z) 

-- | Append two 'ChurchList's. This runs in O(1) time. Note that 
-- there is no need to materialize the lists as @[a]@. 
append :: ChurchList a -> ChurchList a -> ChurchList a 
append xs ys = ChurchList $ \k z -> runList xs k (runList ys k z) 

-- | Map over a 'ChurchList'. No need to materialize the list. 
instance Functor ChurchList where 
    fmap f xs = ChurchList $ \k z -> runList xs (\x xs' -> k (f x) xs') z 

-- | The 'Foldable' instance is trivial, given the 'ChurchList' law. 
instance Foldable ChurchList where 
    foldr f z xs = runList xs f z 

instance Traversable ChurchList where 
    traverse f xs = runList xs step (pure nil) 
     where step x rest = cons <$> f x <*> rest 

Lo svantaggio di questo è che non c'è efficiente tail operazione per un ChurchList -il piegamento una ChurchList è economico, ma prendendo ripetuta code è costoso ...

+0

il 'tail' di un' ChurchList' può essere calcolato, pigramente, in tempo costante. – is7s

+1

Nota che ho detto "prendere code ripetute"; se stai solo prendendo la coda una volta, il semplice 'churchTail = fromList. coda . tolist »non sembra troppo male. Ma ora considera cosa succede con 'churchTail. churchTail': ottieni un 'ChurchList' supportato da un' [] '-list che è costruito da un' ChurchList' supportato da un '[]' -list. Il cuore del problema è che un 'ChurchList' e il suo' churchTail' non condividono la struttura come un '[]' -list e la sua coda fa. Non credo che implementazioni più sofisticate di 'churchTail' che non usano' toList'/'fromList' possano evitare questo. –

+0

Vero, le code ripetute sono costose anche per altre implementazioni. A proposito, non penso che l'operazione 'append' di un' ChurchList' sia migliore di quella di una lista normale, vero? – is7s

Problemi correlati