mercoledì 9 marzo 2011

Gestione dei riferimenti in Perl

La gestione dei riferimenti in Perl è spesso oggetto di errori e discussioni, perciò ho ritenuto interessante elencare come possono essere gestiti i riferimenti dal punto di vista sintattico.
In Perl un riferimento è un valore scalare, concettualmente assimilabile al puntatore del linguaggio C. I riferimenti possono "puntare" ad un array o un hash, e possono essere a loro volta organizzati in array o hash e quindi permettono la creazione di strutture dati complesse (array di array, hash di array, hash di hash, array di hash, ...).
Il riferimento ad una struttura dati (hash o array) non anonima si ottiene mediante l'operatore backslash (\), concettualmente simile all'operatore & del linguaggio C. Essendo un riferimento un valore scalare, il suo valore viene estratto sempre usando il dollaro ($), ossia il riferimento è sempre usato come una normale variabile scalare. Il valore di un riferimento corrisponde al valore del puntatore, ossia alla dereferenziazione del riferimento stesso. Occorre prestare un po' di attenzione ad alcune regole sintattiche quando si lavora con i riferimenti, siano essi ad array o hash. Come regola generale, sempre valida, si può assumere che la dereferenziazione di un riferimento ha sempre la sintassi ${ $nome_riferimento } ossia è come se si avesse l'estrazione del valore del riferimento ($nome_riferimento) a sua volta estratto ancora. Si può pensare che ${ } corrisponda, in Perl, all'operatore * del C (inteso come operatore di "contenuto del puntatore").
In generale quindi vale la regola che ovunque si scriverebbe il nome dell'array o dell'hash referenziato si debba inserire { $nome_riferimento }. Un esempio chiarira' meglio questo aspetto: si supponga di avere un array @array e il suo riferimento $refToArray e si voglia accedere alla sua cella i-esima. La sintassi diventa:

  • accesso diretto $array[ $i ]
  • accesso tramite riferimento ${ $refToarray }[ $i ]

Come si può notare, da un punto di vista sintattico "array" è stato sostituito completamente da "{refToArray }". Questa regola è sempre valida, e quindi la sua applicazione non produce mai errori. E' comunque possibile semplificare ulteriormente la notazione tenendo conto che:
  • se $refToArray è un valore scalare puro allora le parentesi graffe possono essere rimosse, e quindi ${ $refToArray }[ $i ] diventa $$refToArray[ $i ];
  • se la dereferenziazione punta ad un valore scalare, allora ${ REF }[ $i ] puo' essere convertito nella notazione "frecciata" $REF->[$i]. Questa notazione verra' denominata nel seguito "riferimento smart".
Si consideri ora un esempio di codice che riassume le sintassi e le confronta nel caso di array:

# creazione di un array normale
@array = ( 1, 2, 3, 4, 5);

# creo un riferimento (scalare) all'array
$refToArray = \@array;

# estraggo i dati dell'array
print "\nEstrazione dati da array";
for( $i = 0; $i <= $#array; $i++ ){
    print "\n [ $i ] tramite valore            = " . $array[ $i ];
    print "\n [ $i ] tramite riferimento       = " . ${ $refToArray }[ $i ];
    print "\n [ $i ] tramite riferimento       = " . $$refToArray[ $i ];
    print "\n [ $i ] tramite riferimento smart = " . $refToArray->[ $i ];
}
considerazioni fatte per l'array valgono anche nel caso di un hash. Come detto prima, ogni volta che si inserisce il nome dell'hash si può inserire { $refToHash }:



# creo un hash
%hash = ( "key1" => "value1",
      "key2" => "value2",
      "key3" => "value3",
);

# creo un riferimento ad un hash
$refToHash = \%hash;

# estraggo i dati dall'hash
print "\nEstrazione dati hash";
foreach $key ( keys( %hash ) ){
    print "\n [ $key ] tramite valore            = " . $hash{ $key };
    print "\n [ $key ] tramite riferimento       = " . ${ $refToHash }{ $key };
    print "\n [ $key ] tramite riferimento smart = " . $refToHash->{ $key };
}

E' possibile usare i riferimenti anche per la creazione di hash e array anonimi, ossia senza un nome di variabile ma che vengono acceduti solo tramite un riferimento. In questo caso non si usa l'operatore backslash (\) ma rispettivamente:
  • [] per definire un riferimento ad una lista/array;
  • {} per definire un riferimento ad un hash.

Il seguente pezzo di codice mostra come creare riferimenti ad hash e array anonimi, con il relativo accesso ai dati. Si noti ancora una volta come, ovunque sarebbe comparso il nome ipotetico dell'hash, si sia provveduto a sostituire tale nome con il valore scalare del riferimento.

# creazione di un array tramite riferimento
$refToArray = [ "a", "b", "c", "d" ];

for( $i = 0; $i <= $#$refToArray; $i++ ){
    print "\n [ $i ] tramite riferimento       = " . ${ $refToArray }[ $i ];
    print "\n [ $i ] tramite riferimento smart = " . $refToArray->[ $i ];

}

# creazione di un hash tramite riferimento
$refToHash = {
    "key1" => "value1",
    "key2" => "value2",
    "key3" => "value3",
};


foreach $key ( keys( %${refToHash} ) ){
    print "\n [ $key ] tramite valore            = " . $hash{ $key };
    print "\n [ $key ] tramite riferimento       = " . ${ $refToHash }{ $key };
    print "\n [ $key ] tramite riferimento smart = " . $refToHash->{ $key };
}

Nessun commento: