[Roma.pm] xml + tag name

Emanuele Zeppieri ema_zep at libero.it
Tue Jul 24 04:11:57 PDT 2007


Oha wrote:

>> efficiente, visto che Perl non ha la tail recursion optimization 
> 
> a quanto ricordo, la tail recursion e' applicabile solo se la ricorsione e' loop-invariant

Ricordi bene.

> se ricordo bene, quindi, in questo caso la ricorsione avviene piu'
> volte per ogni stato dello stack, e non sarebbe dunque possibile fare
> tail recursion.

In realtà sarebbe possibile, se avessimo per ogni nodo un puntatore al 
padre (però certamente quella mostrata da Aldo non è tail-recursion, 
quindi non è ottimizzabile in automatico).

Ora non credo che nella struttura di dati tirata fuori da XML::Simple ci 
sia il puntatore al padre, però potremmo aggiungerlo con una visita in 
ampiezza (iterativa) molto veloce.

Solo che se utilizziamo la tail-recursion per evitare l'occupazione di 
memoria (lineare nel numero di nodi interni) sullo stack, e poi 
risprechiamo altrettanta memoria (asintoticamente) per il puntatore al 
padre, allora è un cazzo e tutt'uno, per usare un inglesismo.

Quindi percorrere questa strada non ha in effetti molto senso (benché in 
linea teorica sia possibile).

> la soluzione di usare un loop (push su una pila di di cose "da fare") 
> a sua volta potrebbe non esser efficiente (per grandi strutture dati,
> la pila potre diventare enorme)

Qui invece davvero non ti capisco.
Se la pila diventa enorme lo diventa anche nella tua soluzione ed anche 
nella soluzione ricorsiva, pari pari, non si scappa ;-)

L'occupazione di memoria sullo stack è la stessa in tutte le soluzioni: 
una soluzione iterativa, in assenza di puntatori al padre, deve gestirsi 
*per forza* uno stack, e la soluzione ricorsiva gestisce anch'essa né 
più né meno uno stack (sia pure "dietro le quinte"), necessario 
all'interprete perl per allocare le chiamate ricorsive.

> una soluzione e' farsi un vero e proprio stack applicativo,
> mi capito' di scrivere codice del genere in C usando puntatori a struct.
> senza usare OOP un esempio in perl e' quanto segue:

Che cosa vuol dire uno stack applicativo?! :-)
In realtà stai utilizzando uno stack (pressoché) identico a quello che 
ho utilizzato io e a quello necessario a qualsiasi altra soluzione 
iterativa, e altrettanto identico (almeno come dimensioni asintotiche) a 
quello utilizzato dall'interprete perl per allocare le chiamate ricorsive.

> $ref = XMLin('<opt><a/><b><b1 d="0"/></b><c><c1/></c></opt>');
> 
> my @stack = ([$ref, [keys %$ref]]);
> 
> my $sp = 0;
> while($sp>=0)
> {
>         my $r = $stack[$sp][0];
>         my $next = shift @{$stack[$sp][1]};
>         if($next)
>         {
>                 print "child $next...n";
>                 $sp++;
>                 my $r2 = $r->{$next};
>                 $stack[$sp] = [$r2, [keys %$r2]];
>         }
>         else
>         {
>                 $sp--;
>         }
> }
> 
> ogni elemento di @stack contiene lo "stato" dello stack in quel punto.
 >
>la "shift" simula un ciclo prelevando il prossimo elemento nella lista di cose da fare,
 >
>e $sp (come potra' suggerire il nome) e' lo stack pointer.

Non so che vantaggi possa avere quest'implementazione e non so neanche 
con quale altra soluzione la stai confrontando, né su quale piano 
(velocità, occupazione di memoria?)
Peraltro la pop() è leggermente più veloce della shift(), per cui, a 
meno di vincoli sull'ordine di visita dei nodi, ti conviene usare quella 
per la gestione dinamica dello stack.

Per quanto mi riguarda, la soluzione più veloce in assoluto che sono 
riuscito a trovare, puramente iterativa, è la seguente:

my @nodes;
sub iterative {
     @nodes = @_;

     foreach my $node ( @nodes ) {
         foreach my $tag (keys %$node) {
             print "$tag\n";
             push @nodes, $node->{$tag} if ref $node->{$tag}
         }
     }
}
itarative( XMLin( $data, KeepRoot => 1 ) );

E' di uno sfolgorante 8% più veloce di quella ricorsiva di Aldo (alla 
faccia delle sue "forzature" ;-) poi non rischia di incorrere nel 
warning di deep-recursion di perl, ed è sguaiatamente non-HOPpica.

Lo stack non viene mai deallocato, ma in realtà non serve perché è il 
limite superiore di occupazione di memoria che conta, e quello, come già 
detto, è invariante per qualsiasi soluzione.

La visita è diversa (è in ampiezza) e non c'è indentazione nella stampa, 
ma fornisce l'elenco dei tag come originariamente richiesto.

In realtà la ricorsione del Perl è molto più veloce di quanto 
ricordassi, e in questo caso è piuttosto difficile fare di meglio, 
infatti la mia soluzione rispetto a quella di Aldo contiene in 
definitiva solo microottimizzazioni, cercando in realtà di emulare il 
più fedelmente possibile la ricorsione di perl.

Se qualcuno sa fare di meglio si accomodi (use Benchmark)
;-)

P.S.
Il tuo client web-mail, perché non lo butti?

Ciao,
Emanuele.


More information about the Roma mailing list