2013-05-04 6 views
5

Ho una domanda molto noob qui riguardo i refs, anche se comunque mi confondo per lo meno ...
Nell'esempio di codice qui sotto, sto provando a creare un hash di array:
come utilizzare correttamente i riferimenti perl

#!/usr/bin/perl 

use strict; 
use warnings; 
use 5.010; 
use Data::Dumper; 

$Data::Dumper::Sortkeys = 1; 
$Data::Dumper::Terse = 1; 
$Data::Dumper::Quotekeys = 0; 

my @a1 = ('a1', 1, 1, 1); 
my @a2 = ('a2', 2, 2, 2); 

my $a1_ref = \@a1; 
my $a2_ref = \@a2; 

my @a = ($a1_ref, $a2_ref); 

my %h =(); 

for my $i (1 .. 2) { 
     $h{"$i"} = \@a; 
} 

say Dumper \%h; 

L'uscita Dumper è

{ 
      '1' => [ 
        [ 
        'a1', 
        1, 
        1, 
        1 
        ], 
        [ 
        'a2', 
        2, 
        2, 
        2 
        ] 
       ], 
      '2' => $VAR1->{'1'} 
     } 

La domanda qui è:
perché è $ h { '2'} un riferimento a $ h { '1'}? Sto provando a creare un hash% h con valori-chiave identici costituiti dall'array array @a. Voglio che ogni valore-chiave dell'hash abbia il proprio AoA basato su @a, ma sto ottenendo riferimenti a $ h {'1'}. Che cosa sto facendo di sbagliato??
uscita Il Dumper sto provando a achive è:

{ 
      '1' => [ 
        [ 
        'a1', 
        1, 
        1, 
        1 
        ], 
        [ 
        'a2', 
        2, 
        2, 
        2 
        ] 
       ], 
      '2' => [ 
        [ 
        'a1', 
        1, 
        1, 
        1 
        ], 
        [ 
        'a2', 
        2, 
        2, 
        2 
        ] 
       ] 
     } 

Qualsiasi aiuto apprezzato. Grazie in anticipo!
-Dan

risposta

4

Non è che $h{'2'} è un riferimento ad $h{'1'}, ma che entrambi sono riferimenti alla stessa matrice, cioè @a. Che probabilmente si desidera è:

for my $i (1 .. 2) { 
    $h{"$i"} = $a[$i - 1]; 
} 

che equivale a questo:

$h{'1'} = $a[0]; # i.e., $a1_ref 
$h{'2'} = $a[1]; # i.e., $a2_ref 

che fa $h{'1'} un riferimento alla @a1 e $h{'2'} un riferimento a @a2.

Per inciso, si potrebbe trovare utile utilizzare le notazioni [ ... ] e { ... } per creare riferimenti a anonimi array e hash (rispettivamente). Dal momento che non si utilizza mai @a1 e @a2 se non attraverso $a1_ref e $a2_ref, si potrebbe anche creare questi ultimi direttamente:

my $a1_ref = [ 'a1', 1, 1, 1 ]; # reference to a new array (no name needed) 
my $a2_ref = [ 'a2', 2, 2, 2 ]; # ditto 

A cura di domanda aggiornato: Per copiare una matrice, è possibile scrivere:

my @orig = (1, 2, 3); 
my @new = @orig; 

o:

my $orig_ref = [1, 2, 3]; 
my $new_ref = [@$orig_ref]; # arrayref -> array -> list -> array -> arrayref 

Nel tuo caso, se ho capito bene, devi eseguire una copia leggermente "profonda": non vuoi solo due array con gli stessi elementi, vuoi due array i cui elementi sono riferimenti a array distinti con stessi elementi.Non c'è built-in Perl modo per farlo, ma è possibile scrivere un ciclo, oppure utilizzare la funzione map:

my @orig = ([1, 2, 3], [4, 5, 6]); 
my @new = map [@$_], @orig; 

Quindi:

for my $i (1 .. 2) { 
    $h{"$i"} = [map [@$_], @a]; 
} 
+0

Grazie a @ruakh, ma non è esattamente quello che stavo cercando di ottenere. Ho aggiornato il mio post con maggiori informazioni. questo è un buon consiglio per quanto riguarda le notazioni di riferimento. – Gnowl

2

questo (quello che state facendo) farà $h{1} e $h{2} riferiscono alla stessa matrice, @a:

for my $i (1 .. 2) { 
     $h{"$i"} = \@a; 
    } 

Questo renderà $h{1} e $h{2} si riferiscono a due oggetti differenti, ciascuno essendo una copia di@a:

for my $i (1 .. 2) { 
     $h{"$i"} = [ @a ]; 
    } 

Ma le matrici interne saranno ancora alias. Suona come si desidera una copia completa :

2

Penso che questo codice fa quello che si vuole.

Sono passato a Data::Dump perché preferisco la sua uscita.

Il problema con il riferimento ai dati è che, indipendentemente dal numero di volte in cui si copia quel riferimento, si fa riferimento agli stessi dati, quindi una modifica di tali dati viene reindirizzata ovunque venga indicata.

Per produrre una seconda copia indipendente, ad esempio un array, è possibile scrivere my @copy = @array e lavorare da lì. Ma è conveniente usare [ @array ] che copia il contenuto dell'array in un nuovo array anonimo e restituisce un riferimento ad esso.

Si desidera che ciascun valore di hash sia un riferimento a un array a due elementi, ognuno dei quali è un riferimento a un altro array contenente i dati da @a1 e @a2. Questo codice lo farà per te.

Un altro punto è che, poiché le chiavi hash sono stringhe, è insolito utilizzare valori numerici per loro: indica che si dovrebbe invece utilizzare una matrice. Ma dal momento che tutto questo è chiaramente dati segnaposto non ne sono troppo preoccupato.

use strict; 
use warnings; 

use Data::Dump; 

my @a1 = qw/ a1 1 1 1 /; 
my @a2 = qw/ a2 2 2 2 /; 

my %h; 

for my $i (1, 2) { 
    $h{$i} = [ [ @a1 ], [ @a2 ] ]; 
} 

dd \%h; 

uscita

{ 
    1 => [["a1", 1, 1, 1], ["a2", 2, 2, 2]], 
    2 => [["a1", 1, 1, 1], ["a2", 2, 2, 2]], 
} 
0

Tutte le risposte sopra di me hanno indicato la soluzione corretta (e la questione). E questa è la necessità di fare una copia della struttura e quindi usare il riferimento ad essa. Gli array e gli hash che ho indicato qui erano solo segnaposto per una struttura più complicata. Quindi per fare una copia della struttura ho usato il modulo Clone, che fa esattamente una copia profonda di una struttura e restituisce un riferimento diverso ad esso. Grazie a tutti per le risposte!