2013-05-04 2 views
5

У меня есть вопрос о noob-ish здесь, касающийся ссылок, хотя по-прежнему мне мешает, по крайней мере ...
В приведенном ниже примере кода я пытаюсь создать хэш из массивы:
Как правильно использовать ссылки на 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; 

выход Хранитель является

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

вопрос здесь:
почему $ ч { '2'} ссылка на $ ч { '1'}? Я пытаюсь создать хеш% h с идентичными значениями ключа из массива массивов @a. Я хочу, чтобы каждый ключ-значение хэша имел свой собственный AoA, основанный на @a, но вместо этого я получаю ссылки на $ h {'1'}. Что я делаю не так??
Дампер выход я пытаюсь для того чтобы достигнуть это:

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

Любая помощь приветствуется. заранее спасибо!
-дан

ответ

4

Это не то, что $h{'2'} является ссылкой на $h{'1'}, но и ссылки на тот же массив, а именно @a. То, что вы, вероятно, хотите есть:

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

что эквивалентно следующему:

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

, что делает $h{'1'} ссылкой на @a1 и $h{'2'} ссылка на @a2.

Кстати, вы можете обнаружить, что полезно использовать обозначение [ ... ] и { ... } создавать ссылки на анонимных массивы и хэш (соответственно). Поскольку вы никогда не использовать @a1 и @a2 кроме через $a1_ref и $a2_ref, вы можете также создать последний непосредственно:

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

Edited для обновления вопрос: Чтобы скопировать массив, вы можете написать:

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

или:

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

В вашем случае, если вы правильно поняли, вам нужно выполнить слегка «глубокую» копию: вам не просто нужны два массива с одинаковыми элементами, вам нужны два массива, элементы которых являются ссылками на различные массивы с помощью те же элементы.Там нет встроенных в пути Perl, чтобы сделать это, но вы можете написать цикл, или использовать map функцию:

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

Итак:

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

Спасибо @ruakh, но это не совсем то, чего я пытался достичь. Я обновил свой пост с дополнительной информацией. это хороший совет относительно ссылочных обозначений. – Gnowl

2

Это (то, что вы делаете) будет делать $h{1} и $h{2} относится к тому же массиву, @a:

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

Это сделает $h{1} и $h{2} относятся к двум разным объектам, каждый из которых копию из@a:

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

Но внутренние массивы по-прежнему будет псевдонимами. Похоже, что вы хотите глубокой копии:

2

Я думаю, что этот код делает то, что вы хотите.

Я переключился на Data::Dump, потому что предпочитаю его выход.

Проблема с ссылкой на данные заключается в том, что независимо от того, сколько раз вы копируете эту ссылку, она по-прежнему указывает на одни и те же данные, поэтому изменение этих данных происходит во всех случаях, когда оно упоминается.

Чтобы создать вторую независимую копию, скажем, массива, вы можете, конечно, написать my @copy = @array и работать оттуда. Но удобно использовать [ @array ], который копирует содержимое массива в новый анонимный массив и возвращает ссылку на него.

Вы хотите, чтобы каждое хеш-значение являлось ссылкой на двухэлементный массив, каждый из которых является ссылкой на другой массив, содержащий данные от @a1 и @a2. Этот код сделает это за вас.

Другим моментом является то, что, поскольку хеш-ключи являются строками, необычно использовать числовые значения для них: это указывает на то, что вместо этого вы должны использовать массив. Но так как все это явно данные заполнителя, я не слишком беспокоюсь об этом.

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; 

выходные

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

Все ответы выше указал мне правильное решение (и выпуска). И это необходимо сделать копию структуры, а затем использовать ссылку на нее. Массивы и хеши, которые я указал здесь, были просто заполнитель для более сложной структуры. Итак, чтобы сделать копию структуры, я использовал модуль Clone, который выполняет точно глубокую копию структуры и возвращает другую ссылку на нее. Спасибо всем за ответы!

Смежные вопросы