Питання Яка точка такого коду?


{%{$self->param}}

Розширюється хеш, а потім створюється інша хеш-посилання.

Але це не так {%{$self->param}} такий же як і $self->param? Чому код перешкоджає робити цей трюк?


9
2017-08-29 06:57


походження


Чи працює код у вашому додатку в обох напрямках? Якщо ні, то вони не такі самі. На жаль, зворотне не завжди вірно - EnabrenTane


Відповіді:


Він копіює хеш. Розглянемо наступний фрагмент:

use Data::Dumper;

my $foo = { a => 1, ar => [1] };
my $bar = {%$foo};

$bar->{b} = 2;
push @{$bar->{ar}}, 4;

print Dumper $foo;
print Dumper $bar;

Це друкує

$VAR1 = {
          'a' => 1,
          'ar' => [
                    1,
                    4
                  ]
        };
$VAR1 = {
          'a' => 1,
          'b' => 2,
          'ar' => [
                    1,
                    4
                  ]
        };

Таким чином, ви можете побачити, що копія є мілкою: скаляри копіюються, навіть якщо вони є посиланнями. Об'єкти, на які посилаються, однакові (у цьому прикладі масив з посиланням на ar)


15
2017-08-29 07:16



Це робиться дрібна копія; якщо деякі з цінностей є посиланнями, дані, на які посилаються ці користувачі, залишаться спільними між двома хешами. - ysth
Не забудьте згадати, що це робить неглибокий (ні глибоко) копія - напр. значення хешу, які є самими посиланнями, зберігатимуться у початкових місцях у вашій новій копії та не будуть копіюватися, а також оригінальний хеш - DVK
@ysth: Так, хороший момент. Я додав це. - musiKk


Хоча обидва {%{$self->param}} і $self->param є посиланнями на хеш, вони не посилаються на хеш, збережені в одному місці.

Перший вислів dereferences $self->param до хешу і повертає посилання на анонімний хеш. У межах зовнішніх фіксаторів %{$self->param} фактично розширена і скопійована тимчасово, а потім повертається посилання на цю тимчасову копію, а не на старий хеш.


8
2017-08-29 07:15





Цей код насправді створює a скопіювати хеш (мілкова копія ключів і значень, але не глибока копія), посилання на яку повертається і повертає посилання на нього.

Якщо якийсь sub повертає посилання на хеш, і ви щось змінюєте, ви дійсно змінюєте значення в оригінальному хеш. Щоб уникнути цього, нам іноді потрібно скопіювати весь хеш (або масив) перед внесенням будь-яких змін.

Ось приклад:

sub get_hashref {
    my $hashref = shift;
    return $hashref;
}

my %hash = (foo => 'bar');

my $ref = get_hashref(\%hash);
$ref->{foo} = 'baz'; # Changes 'foo' value in %hash
print "Original 'foo' now is: $hash{foo}\n"; # 'baz'
print "Ref's 'foo' now is: $ref->{foo}\n";   # 'baz'

# But!

$ref = {%{ get_hashref(\%hash) }};
$ref->{foo} = 42; # No changes in %hash
print "Original 'foo' now is: $hash{foo}\n"; # 'baz'
print "Ref's 'foo' now is: $ref->{foo}\n";   # '42'

Щоб бути краще зрозумілим {%{ $self->param }} може бути розширений до:

my $ref = $self->param; # Ref to original hash
my %copy = %{$ref}; # Copies keys and values to new hash
my $ref_to_copy = {%copy}; # get ref to it

Ви також можете пропустити останній крок, якщо вам потрібен хеш, але не посилання на нього.


8
2017-08-29 07:16



Не забудьте згадати, що це робить неглибокий (ні глибоко) копія - напр. значення хешу, які є самими посиланнями, зберігатимуться у початкових місцях у вашій новій копії та не будуть копіюватися, а також оригінальний хеш - DVK
Дякую. Мені було цікаво, як сказати це правильно. - yko