Поведение клавиш хэша

perl -Mstrict -wlE 'my %h; say grep 0, $h{poluted}; say keys %h'

Выход

poluted

и

perl -Mstrict -wlE 'my %h; say grep 0, my @r= $h{poluted}; say keys %h'

не выводит результат.

Я хотел бы знать, почему выходы разные?

Ответ 1

Искажения

В Perl-строках цикла map, grep и for переменная $_ присваивается каждому текущему элементу. Пока $_ может быть только для чтения, он всегда представляет допустимое скалярное значение.

Например, следующий код умирает:

$_ = 1 for 1, 2, 3;  # constants are read-only

но это работает:

my @nums = (1, 2, 3);
$_ = 1 for @nums;  # @nums isn't read-only

Обратите внимание, что присваивания выполняют копию, но псевдоним связывает имя с существующим скаляром.

Два значения undef

Perl имеет два типа undef:

  • Скаляр может быть установлен как undef. Например:

    my $foo;  # is this kind of undef
    $foo = 1; # isn't undef any more
    
  • Специальный глобально уникальный скаляр, который представляет значение readonly undef, например. возвращается при доступе к индексу uninitialized array в контексте rvalue. В API Perl это &PL_sv_undef. Вы можете получить ссылку на это значение, например. \undef и может содержать псевдоним переменной.

Два способа доступа к хеш-значению

Внутри хэш-записи выбираются с помощью hv_fetch или hv_fetch_ent. В качестве аргументов оба принимают хеш, ключ и флаг, сообщающий им, доступен ли доступ только для чтения.

Если это доступ только для чтения, и элемент не существует, возвращается нулевой указатель, который проявляется как значение undef в пространстве Perl. Это значение undef не связано с хешем. Ergo, not exists $hash{foo} означает not defined $hash{foo}.

Но если он не доступен только для чтения и элемент не существует, создается новая запись, которая затем возвращается. Тем не менее, эта запись первоначально undef, пока она не будет установлена ​​на другое значение через назначение.

Итак, почему код в вопросе не работает?

grep 0, $h{polluted}

Списки аргументов для строковых конструкций сглажены до $_. Если выражения в списке являются константами или подпрограммами, то ничего эффектного не происходит. Но когда они являются переменными доступом, это подразумевает доступ на чтение и запись.

Итак, чтобы получить значение $h{polluted}, Perl, очевидно, делает доступ в режиме чтения-записи. Если мы посмотрим на коды операций для этого выражения, мы действительно видим:

3  <0> pushmark s
4  <#> gv[*h] s
5  <1> rv2hv sKR/1
6  <$> const[PV "polluted"] s/BARE
7  <2> helem sKM/2                # <-- hash element access, "M" flag is set!
8  <@> grepstart K
9  <|> grepwhile(other->a)[t2] vK
a      <$> const[IV 0] s
           goto 9

M означает MOD, что означает доступ к lvalue/read-write.

Почему это поведение делает "смысл"

В for -loops, имеющий $_, является псевдонимом текущего элемента, может быть действительно полезным. В map и grep это взлом производительности, чтобы избежать копирования всего скаляра. Алиасинг намного дешевле, так как это подразумевает только копию одного указателя.