index | project | pods | responsabili |
NOMEperllol - Manipolare Array di Array in Perl
DESCRIZIONE
Dichiarazione ed Accesso ad Array di ArrayLa cosa più semplice da costruire è un array di array (a volte detto imprecisamente lista di liste). È piuttosto semplice da comprendere, e praticamente tutto quello che si applica qui sarà anche applicabile nel seguito a strutture dati più elaborate. Un array di array è solo un buon vecchio array # assegnazione al nostro array, un array di riferimenti ad array @AoA = ( [ "fred", "barney" ], [ "george", "jane", "elroy" ], [ "homer", "marge", "bart" ], ); print $AoA[2][2]; bart Dovete prestare molta attenzione al fatto che la parentesi esterna è
tonda; questo dipende dal fatto che state effettuando un'assegnazione
ad un un # assegna un riferimento ad un array di riferimenti ad array $rif_a_AdA = [ [ "fred", "barney", "pebbles", "bambam", "dino", ], [ "homer", "bart", "marge", "maggie", ], [ "george", "jane", "elroy", "judy", ], ]; print $rif_a_AdA->[2][2]; Osservate che il tipo delle parentesi esterne è cambiato da tondo
a quadrato, per cui la nostra sintassi di accesso è variata. Questo
è necessario per il fatto che, diversamente da C, in Perl non potete
scambiare liberamente array e riferimenti ad essi. $AoA[2][2] $rif_a_AdA->[2][2] invece di essere costretti ad utilizzare: $AoA[2]->[2] $rif_a_AdA->[2]->[2] Bene, questo dipende dalla regola che dice che, limitatamente a parentesi
adiacenti (siano esse quadrate o graffe), siete liberi di omettere la freccia
di dereferenziazione. Ma non potete farlo per la prima parentesi se il
riferimento è contenuto in uno scalare, il che significa che
Primi passi in autonomiaFin qui tutto a posto per la dichiarazione di una struttura dati fissa, ma cosa fare se avete bisogno di aggiungere nuovi elementi al volo, o costruire la struttura da zero? Prima di tutto vediamo come leggerla da file. È qualcosa di molto
simile ad aggiungere una riga per volta. Assumeremo che sia presente un
semplice file nel quale ciascuna linea è una riga della nostra struttura,
e ciascuna parola un elemento. Se state provando a costruire un array
while (<>) { @tmp = split; push @AoA, [ @tmp ]; } Potete anche caricare i dati da una funzione: for $i ( 1 .. 10 ) { $AoA[$i] = [ una_funzione($i) ]; } In alternativa, potreste utilizzare una variabile temporanea con il contenuto dell'array: for $i ( 1 .. 10 ) { @tmp = una_funzione($i); $AoA[$i] = [ @tmp ]; } È molto importante che vi assicuriate di utilizzare il costruttore
di riferimento ad array $AoA[$i] = @tmp; Come potete vedere, assegnare un array ad uno scalare (perché Se state utilizzando use strict; my(@AoA, @tmp); while (<>) { @tmp = split; push @AoA, [ @tmp ]; } È chiaro che non avete bisogno di avere un array temporaneo esplicito: while (<>) { push @AoA, [ split ]; } Non avete neanche bisogno di utilizzare my (@AoA, $i, $line); for $i ( 0 .. 10 ) { $line = <>; $AoA[$i] = [ split ' ', $line ]; } In questo caso basta anche: my (@AoA, $i); for $i ( 0 .. 10 ) { $AoA[$i] = [ split ' ', <> ]; } In generale, però, conviene essere cauti nell'utilizzare funzioni che potrebbero potenzialmente restituire liste in un contesto scalare, senza che questo risulti in quale modo esplicito. Per il lettore occasionale quanto segue sarebbe più comprensibile: my (@AoA, $i); for $i ( 0 .. 10 ) { $AoA[$i] = [ split ' ', scalar(<>) ]; } Se voleste avere una variabile while (<>) { push @$rif_a_AdA, [ split ]; } Ora sapete come aggiungere nuove righe. Che ne pensate di aggiungere nuove colonne? Se stiamo parlando di semplici matrici, è spesso più facile utilizzare una semplice assegnazione: for $x (1 .. 10) { for $y (1 .. 10) { $AoA[$x][$y] = func($x, $y); } } for $x ( 3, 7, 9 ) { $AoA[$x][20] += func2($x); } Non importa se gli elementi sono già lì o no: perl sarà felice di crearli
per voi, impostando tutti gli elementi in mezzo necessari a Se voleste solamente aggiungere elementi ad una riga, dovreste fare qualcosa di un po' più articolato: # aggiungere nuove colonne ad una riga push @{ $AoA[0] }, "wilma", "betty"; Da notare che non abbiamo potuto dire semplicemente: push $AoA[0], "wilma", "betty"; # SBAGLIATO! Questa espressione, infatti, non compilerebbe nemmeno. Com'è possibile?
Perché il primo argomento di
Accesso ai dati e StampaÈ ora di stampare la vostra struttura dati. Come farlo? Se volete solo un elemento è banale: print $AoA[0][0]; Se volete stampare tutto, però, non potete scrivere semplicemente print @AoA; # SBAGLIATO perché ricevereste solo la lista dei riferimenti, e perl non
effettuerà mai una dereferenziazione implicita per conto vostro. Dovete
mettere su uno o due cicli per conto vostro, dunque; il
codice che segue stampa l'intera struttura, utilizzando for $aref ( @AoA ) { print "\t [ @$aref ],\n"; } Se voleste tener traccia degli indici esplicitamente, potreste fare come segue: for $i ( 0 .. $#AoA ) { print "\t la riga $i contiene [ @{$AoA[$i]} ],\n"; } o anche così (notate il ciclo interno): for $i ( 0 .. $#AoA ) { for $j ( 0 .. $#{$AoA[$i]} ) { print "l'elemento in posizione $i $j vale $AoA[$i][$j]\n"; } } Ebbene sì, comincia a diventare un po' complicato. Questo è il motivo per cui a volte è più semplice (e leggibile) utilizzare qualche variabile temporanea lungo il percorso: for $i ( 0 .. $#AoA ) { $aref = $AoA[$i]; for $j ( 0 .. $#{$aref} ) { print "l'elemento in posizione $i $j vale $AoA[$i][$j]\n"; } } Rimane ancora un po' bruttino. Che ne pensate di questo? for $i ( 0 .. $#AoA ) { $aref = $AoA[$i]; $n = @$aref - 1; for $j ( 0 .. $n ) { print "l'elemento in posizione $i $j vale $AoA[$i][$j]\n"; } }
SliceSe volete accedere ad una slice (una porzione di una riga costituita da elementi in posizioni adiacenti) in un array multidimensionale avrete bisogno di utilizzare un po' di indicizzazione ed un po' di fantasia. Se infatti abbiamo a disposizione un sinonimo comodo per accedere ad un singolo elemento, utilizzando la freccia di puntamento per la dereferenziazione, non esiste nessuno strumento simile per le slice. (Ricordate, ovviamente, che potete sempre scrivere un ciclo per effettaure l'estrazione di una slice). Ecco come effettuare un'operazione (estrazione dei dati, nel caso particolare)
utilizzando un ciclo. Assumeremo
che @part = (); $x = 4; for ($y = 7; $y < 13; $y++) { push @part, $AoA[$x][$y]; } Tale ciclo può essere rimpiazzato da un'operazione effettuata su una slice opportuna: @part = @{ $AoA[4] } [ 7..12 ]; ma, come avete già indovinato, è piuttosto criptico per chi legge. Che dovreste fare se voleste una slice bidimensionale, come
prendere i dati con @newAoA = (); for ($startx = $x = 4; $x <= 8; $x++) { for ($starty = $y = 7; $y <= 12; $y++) { $newAoA[$x - $startx][$y - $starty] = $AoA[$x][$y]; } } Possiamo ridurre i cicli espliciti utilizzando le slice: for ($x = 4; $x <= 8; $x++) { push @newAoA, [ @{ $AoA[$x] } [ 7..12 ] ]; } Se avete dimestichezza con la Schwartzian Transform (Trasformazione
di Schwartz, da Randal L. Schwartz, N.d.T.), avreste
probabilmente scelto @newAoA = map { [ @{ $AoA[$_] } [ 7..12 ] ] } 4 .. 8; Per quanto sarebbe difficile, in questo caso, controbattere il vostro capo, se vi accusasse di cercare la certezza di conservare il vostro posto di lavoro (o perderlo molto rapidamente) utilizzando codice illeggibile. Fossi in voi, lo incapsulerei all'interno di una funzione: @newAoA = splice_2D( \@AoA, 4 => 8, 7 => 12 ); sub splice_2D { my $lrr = shift; # rif. ad un array di rif. ad array! my ($x_lo, $x_hi, $y_lo, $y_hi) = @_; return map { [ @{ $lrr->[$_] } [ $y_lo .. $y_hi ] ] } $x_lo .. $x_hi; }
VEDERE ANCHEperldata(1), perlref(1),
AUTORETom Christiansen <tchrist@perl.com> Last update: Thu Jun 4 16:16:23 MDT 1998
TRADUZIONE
VersioneLa versione su cui si basa questa traduzione è ottenibile con: perl -MPOD2::IT -e print_pod perllol Per maggiori informazioni sul progetto di traduzione in italiano si veda http://pod2it.sourceforge.net/ .
TraduttoreTraduzione a cura di Flavio Poletti.
RevisoreRevisione a cura di dree. Mon Jun 11 22:02:16 2012 |