Что `$hash{$key} |= {}` делает в Perl?

Я боролся с каким-то Perl, который использует хеш-ссылки.

В итоге оказалось, что моей проблемой была строка:

$myhash{$key} |= {};

То есть "присвойте $myhash{$key} ссылку на пустой хеш, если у него уже нет значения".

Однако разыменование этого и попытка использовать его в качестве хеш-ссылки приводят к ошибкам интерпретатора при использовании строки в качестве хеш-ссылки.

Меняя это на:

if( ! exists $myhash{$key}) {
  $myhash{$key} = {};
}

... заставил вещи работать.

Так что у меня нет проблем. Но мне любопытно, что происходит.

Кто-нибудь может объяснить?

4 ответа

Решение

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

  $foo |= $bar;

такой же как

  $foo = $foo | $bar

В вашем примере происходит то, что ваша новая анонимная хеш-ссылка становится строковой, а затем побитовым ИЛИ со значением $myhash{$key}, Чтобы запутать дело дальше, если $myhash{$key} в то время не определено, значение - простая строковая ссылка на хеш, которая выглядит следующим образом HASH(0x80fc284), Поэтому, если вы проведете краткий осмотр структуры, она может выглядеть как ссылка на хеш, но это не так. Вот несколько полезных выводов через Data::Dumper:

   perl -MData::Dumper -le '$hash{foo} |= { }; print Dumper \%hash'
   $VAR1 = {
             'foo' => 'HASH(0x80fc284)'
           };

И вот что вы получаете, когда используете правильный оператор:

  perl -MData::Dumper -le '$hash{foo} ||= { }; print Dumper \%hash'
  $VAR1 = {
            'foo' => {}
          };

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

Начиная с Perl 5.10 лучше использовать //= вместо. // является логически определенным оператором "или" и не терпит неудачу в угловом случае, где определено текущее значение, но false.

Я думаю, что ваша проблема использовала |= "(побитовое или присваивание) вместо" ||= "(присвойте, если false).

Обратите внимание, что ваш новый код не совсем эквивалентен. Разница в том, что $myhash{$key} ||= {} "заменит существующие-но-ложные значения хеш-ссылкой, а новое - нет. На практике это, вероятно, не имеет значения.

Попробуй это:

my %myhash;
$myhash{$key} ||= {};

Вы не можете объявить хеш- элемент в my пункт, насколько я знаю. Сначала вы объявляете хеш, а затем добавляете элемент в.

Редактировать: я вижу, вы вынули my, Как насчет попытки ||= вместо |=? Первое идиоматично для "ленивой" инициализации.

Другие вопросы по тегам