Sono interessato ad algoritmi funzionali efficienti (preferibilmente in Haskell, e ancor più preferibilmente già implementati come parte di una libreria!) Per calcolare la chiusura di un contenitore con un operatore unario.Algoritmo funzionale efficiente per il calcolo della chiusura sotto un operatore
un esempio di base e inefficiente di quello che ho in mente, per le liste, è:
closure :: Ord a => (a -> a) -> [a] -> [a]
closure f xs = first_dup (iterate (\xs -> nub $ sort $ xs ++ map f xs) xs) where
first_dup (xs:ys:rest) = if xs == ys then xs else first_dup (ys:rest)
Un'implementazione più efficiente mantiene tracce dei nuovi elementi generati in ogni fase (la "frangia") e doesn 't applicare la funzione di elementi di cui è già stato applicato:
closure' :: Ord a => (a -> a) -> [a] -> [a]
closure' f xs = stable (iterate close (xs, [])) where
-- return list when it stabilizes, i.e., when fringe is empty
stable ((fringe,xs):iterates) = if null fringe then xs else stable iterates
-- one iteration of closure on (fringe, rest); key invariants:
-- (1) fringe and rest are disjoint; (2) (map f rest) subset (fringe ++ rest)
close (fringe, xs) = (fringe', xs') where
xs' = sort (fringe ++ xs)
fringe' = filter (`notElem` xs') (map f fringe)
ad esempio, se xs
è un elenco secondario vuoto di [0..19]
, quindi closure' (\x->(x+3)`mod`20) xs
è [0..19], e l'iterazione stabilizza in 20 passaggi per [0]
, 13 passaggi per [0,1]
e 4 passaggi per [0,4,8,12,16]
.
È possibile ottenere un'efficienza ancora maggiore utilizzando un'implementazione di serie ordinata basata su albero. È già stato fatto? Che ne è della relativa ma più difficile domanda di chiusura sotto operatori binari (o di più alto livello)?
ha importanza la pigrizia? –
Non nelle applicazioni che ho in mente, dove ho un set finito che è già chiuso e voglio calcolare la chiusura di un sottoinsieme. – lambdacalculator