Ответ 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
это взлом производительности, чтобы избежать копирования всего скаляра. Алиасинг намного дешевле, так как это подразумевает только копию одного указателя.