Почему отправка хеша в массив перезаписывает все элементы массива? - PullRequest
3 голосов
/ 06 февраля 2012

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

use Data::Dumper;

my %val;

%val = (key1 => "Val1");

my @myArr;

my $cnt = push(@myArr,\%val);

print "After push (should contain 1 element): " . Dumper(@myArr) . "\n";

%val = (key2 => "Val2");

my $cnt = push(@myArr,\%val);

print "After push 2: (should contain 2 different elements):" . Dumper(@myArr) . "\n";
print " You can see above that element 1 and 2 of the array equal each other when they should be different\n";

Ответы [ 4 ]

18 голосов
/ 06 февраля 2012

"ошибка в Perl" - да, большой шанс.: -)

Вы помещаете ссылку на хеш в ваш массив, затем меняете этот самый хэш, а затем вы снова нажимаете эту же ссылку.

Возможно, вам нужна копияили совершенно другой хэш:

Другая переменная:

#!/usr/bin/perl

use strict; # always use strict
use warnings;
use Data::Dumper;

my ( %val, %other_val, @myArr );

%val       = ( key1 => "Val1" );
%other_val = ( key2 => "Val2" );

push(@myArr, \%val);
push(@myArr, \%other_val);

print Dumper(\@myArr) . "\n";

Копирование:

#!/usr/bin/perl

use strict; # always use strict
use warnings;
use Data::Dumper;

my ( %val, %other_val, @myArr );

%val = ( key1 => "Val1" );
push(@myArr, { %val } );

%val = ( key2 => "Val2" );
push(@myArr, { %val } );


print Dumper(\@myArr) . "\n";
2 голосов
/ 06 февраля 2012

Заметьте, как вы отправляете ссылку на хеш% val? Что ж, если вы измените этот хеш, ссылка, естественно, будет указывать на другое значение.

0 голосов
/ 07 июня 2018

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

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;

, а затем вы получите ссылку на него:

my $personRef = \%person;

В большинстве языков программирования это утверждение читается как " Получите адрес памятирезервное хранилище, используемое %person и записать его в $personRef ".В этих языках я могу затем изменить %person сам (а не память, на которую он указывает), и это не окажет никакого влияния на $personRef, все еще указывая на ту же память, что и раньше, которая все еще содержит те же значения, что и раньше.

Но в Perl это утверждение скорее читается как " Позвольте $personRef всегда указывать на память, используемую в настоящее время %person".Важное ключевое слово здесь всегда, так как это означает, что если вы вносите изменения в %person, эти изменения также немедленно отражаются ссылкой.

Таким образом, когда вы делаете это в Perl:

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;

my $personRef = \%person;

%person = (); # <-- Assigns new empty hash!
$person{"name"} = "Jane Doe";
$person{"age"} = 47;

print "$personRef->{'name'}\n";

На самом деле он напечатает «Jane Doe», поскольку независимо от того, что вы делаете с %person, $personRef всегда будет указывать на используемую память %person, даже когда вы назначаете новый пустой хеш для %person, ссылка теперь будет указывать на этот пустой хеш.

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

Принятый ответ решает эту проблемукопируя хэши:

my @people = ();

my %person = ();
$person{"name"} = "John Doe";
$person{"age"} = 34;
push @people, { %person };

%person = ();
$person{"name"} = "Jane Doe";
$person{"age"} = 47;
push @people, { %person };

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

my %hash = (
    "name" => "John Doe",
    "age" => 34
);

Но это всего лишь синтаксический сахар, поскольку на самом деле вы также можете использовать , вместо =>:

my %hash = ("name", "John Doe", "age", 34);

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

my $hashRef = { "name", "John Doe", "age", 34 };

И это в основном то же самое, что и запись:

my $hashRef = { %myHash };

Это читается как " Создайте новый хеш ({}), заполните его значениями от %myHash и напишите ссылку на него $hashRef ".Это именно то, что происходит в этой строке:

push @people, { %person };

За исключением того, что ссылка на вновь созданный хэш непосредственно помещается в массив.

0 голосов
/ 06 февраля 2012

Это очень естественно: вы используете один и тот же контейнер %val дважды.Вы явно переопределяете его содержимое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...