In che modo la "deforestazione" rimuove gli "alberi" da un programma?


12

Penso che comprenda come la deforestazione consuma e produce un elenco allo stesso tempo (da una piega e una funzione spiegata - vedi questa buona risposta su CodeReview qui ), ma quando ho confrontato quello con la voce di Wikipedia sulla tecnica ha parlato di "rimozione alberi "da un programma.

Capisco come un programma può essere analizzato in un albero di analisi sintattica (è giusto?), Ma qual è il significato di questo uso della deforestazione per un qualche tipo di semplificazione (è?) Dei programmi? E come lo farei con il mio codice?

Risposte:


9

Yatima2975 sembra aver coperto le tue prime due domande, proverò a coprire la terza. Per fare questo tratterò un caso irrealisticamente semplice, ma sono sicuro che sarai in grado di immaginare qualcosa di più realistico.

Immagina di voler calcolare la profondità dell'intero albero binario dell'ordine . Il tipo di alberi binari (senza etichetta) è (nella sintassi di Haskell):n

type Tree = Leaf | Node Tree Tree

n

full : Int -> Tree
full n | n == 0 = Leaf
full n = Node (full (n-1)) (full (n-1))

E la profondità di un albero è calcolata da

depth : Tree -> Int
depth Leaf = 0
depth (Node t1 t2) = 1 + max (depth t1) (depth t2)

depth (full n)nfulldepthdepth (full n)full_depth

full_depth : Int -> Int
full_depth n | n == 0 = 0
full_depth n = 1 + max (full_depth (n-1)) (full_depth (n-1))

Ciò evita l'allocazione di memoria dell'intero albero e la necessità di eseguire la corrispondenza dei modelli, migliorando notevolmente le prestazioni. Inoltre, se aggiungi l'ottimizzazione

max t t --> t

full_depth

L'unico compilatore tradizionale che esegue deforestazione automatica è GHC, e se ricordo bene, questa viene eseguita solo quando compone incorporato funzioni (per motivi tecnici).


Premiato perché ho ottenuto di più da questa risposta dal modo in cui è stata formulata che dalle altre risposte, anche se essenzialmente coprono lo stesso territorio.
Cris Stringfellow

6

Innanzitutto, le liste sono una specie di alberi. Se rappresentiamo un elenco come un elenco collegato , è solo un albero il cui ogni nodo ha 1 o 0 discendenti.

Gli alberi di analisi sono solo un utilizzo degli alberi come struttura di dati. Gli alberi hanno molte applicazioni nell'informatica, inclusi smistamento, implementazione di mappe, array associativi, ecc.

In generale, elenco, alberi, ecc. Sono strutture di dati ricorsive: ogni nodo contiene alcune informazioni e un'altra istanza della stessa struttura di dati. La piegatura è un'operazione su tutte queste strutture che trasforma ricorsivamente i nodi in valori "dal basso verso l'alto". Il dispiegamento è il processo inverso, converte i valori in nodi "top down".

Per una data struttura di dati, possiamo costruire meccanicamente le loro funzioni di piegatura e spiegamento.

Ad esempio, prendiamo le liste. (Userò Haskell per gli esempi mentre viene digitato e la sua sintassi è molto pulita.) L'elenco è una fine o un valore e una "coda".

data List a = Nil | Cons a (List a)

Ora immaginiamo di piegare un elenco. Ad ogni passo, abbiamo il nodo corrente da piegare e abbiamo già piegato i suoi sotto-nodi ricorsivi. Possiamo rappresentare questo stato come

data ListF a r = NilF | ConsF a r

dove rè il valore intermedio costruito piegando la lista secondaria. Questo ci consente di esprimere una funzione di piegatura su liste:

foldList :: (ListF a r -> r) -> List a -> r
foldList f Nil            = f NilF
foldList f (Cons x xs)    = f (ConsF x (foldList f xs))

Convertiamo Listin ListFricorsivamente ripiegando sua sottolista e quindi utilizzare una funzione definita ListF. Se ci pensate, questa è solo un'altra rappresentazione dello standard foldr:

foldr :: (a -> r -> r) -> r -> List a -> r
foldr f z = foldList g
  where
    g NilF          = z
    g (ConsF x r)   = f x r

Possiamo costruire unfoldListallo stesso modo:

unfoldList :: (r -> ListF a r) -> r -> List a
unfoldList f r = case f r of
                  NilF        -> Nil
                  ConsF x r'  -> Cons x (unfoldList f r')

Ancora una volta, è solo un'altra rappresentazione di unfoldr:

unfoldr :: (r -> Maybe (a, r)) -> r -> [a]

(Si noti che Maybe (a, r)è isomorfo a ListF a r.)

E possiamo anche costruire una funzione di deforestazione:

deforest :: (ListF a r -> r) -> (s -> ListF a s) -> s -> r
deforest f u s = f (map (deforest f u) (u s))
  where
    map h NilF        = NilF
    map h (ConsF x r) = ConsF x (h r)

Lascia semplicemente fuori l'intermedio Liste fonde insieme le funzioni di piegatura e spiegamento.

La stessa procedura può essere applicata a qualsiasi struttura di dati ricorsiva. Ad esempio, un albero i cui nodi possono avere 0, 1, 2 o discendenti con valori su nodi con ramificazione 1- o 0:

data Tree a = Bin (Tree a) (Tree a) | Un a (Tree a) | Leaf a

data TreeF a r = BinF r r | UnF a r | LeafF a

treeFold :: (TreeF a r -> r) -> Tree a -> r
treeFold f (Leaf x)       = f (LeafF x)
treeFold f (Un x r)       = f (UnF x (treeFold f r))
treeFold f (Bin r1 r2)    = f (BinF (treeFold f r1) (treeFold f r2))

treeUnfold :: (r -> TreeF a r) -> r -> Tree a
treeUnfold f r = case f r of
                  LeafF x         -> Leaf x
                  UnF x r         -> Un x (treeUnfold f r)
                  BinF r1 r2      -> Bin (treeUnfold f r1) (treeUnfold f r2)

Certo, possiamo creare deforestTreemeccanicamente come prima.

(Di solito, esprimiamo treeFoldpiù convenientemente come:

treeFold' :: (r -> r -> r) -> (a -> r -> r) -> (a -> r) -> Tree a -> r

)

Lascerò fuori i dettagli, spero che lo schema sia evidente.

Guarda anche:


Ottima risposta, grazie. I collegamenti e l'esempio dettagliato sono preziosi.
Cris Stringfellow

3

È un po 'confuso, ma viene applicata la deforestazione (in fase di compilazione) per eliminare gli alberi intermedi che verrebbero creati (in fase di esecuzione). La deforestazione non comporta l'hacking di parti dell'albero di sintassi astratto (eliminazione del ramo morto :-)

Un'altra cosa che potrebbe averti buttato via è che gli elenchi sono alberi, solo molto squilibrati!


Ah sì. Molto sbilanciato!
Cris Stringfellow,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.