Когда Perl автоматически инициализирует переменные? - PullRequest
8 голосов
/ 04 ноября 2008

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

use warnings;
use strict;

my @data = qw(1 1 2 3 4 5 5 5 9);
my %histogram;
foreach (@data)
{
    $histogram{$_}++;
}

Когда тело цикла изменяется на

$histogram{$_} = $histogram{$_} + 1;

Perl предупреждает «Использование неинициализированного значения дополнительно».

Что происходит под капотом? Почему значение инициализируется, когда оно передается как операнд оператору ++ и не инициализируется оператором +?

Ответы [ 4 ]

15 голосов
/ 04 ноября 2008

Оператор + оценивает как форму слева, так и форму справа от нее, а затем возвращает сумму обоих. Оценка вызова хеша не видит никакого специального контекста.

В оператор ++ встроена особая магия. Цитата из справочной страницы perlop относительно оператора ++:

"undef" всегда обрабатывается как числовое, и, в частности, перед увеличением увеличивается на 0 (так что постинкремент значения undef вернет 0, а не "undef").

edit : Чтобы уточнить разницу, ++ изменяет значение на месте, а + просто принимает его аргументы в качестве входных данных. Когда + видит неопределенное значение, обычно что-то идет не так, но для ++ ваш пример манипулирования хешем очень типичен - пользователь хочет рассматривать undef как 0 вместо того, чтобы проверять и инициализировать каждый раз. Так что, кажется, имеет смысл обрабатывать эти операторы таким образом.

8 голосов
/ 04 ноября 2008

Некоторые операторы намеренно пропускают «неинициализированное» предупреждение для вашего удобства. потому что они обычно используются в ситуациях, когда значение по умолчанию 0 или «» для левого или единственного операнда имеет смысл.

Это: ++ и - (до или после), + =, - =,. =, | =, ^ =, && =, || =.

Обратите внимание, что некоторые из них ошибочно выдают предупреждение при использовании в связанной переменной: см. Тесты, помеченные как TODO в http://perl5.git.perl.org/perl.git/blob/HEAD:/t/op/assignwarn.t.

8 голосов
/ 04 ноября 2008

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

В этом случае, как сказал Арлекин, операторы автоинкремента имеют особый случай.

0 голосов
/ 04 ноября 2008

Как сказал Брайан: он все еще делает это, он просто предупреждает вас. Предупреждения говорят вам об определенных манипуляциях с эффектами, которые вы, возможно, не предполагали.

Вы конкретно запрашиваете значение $histogram{$_}, добавляете 1 к нему и затем назначаете его на тот же слот. То же самое, что я не ожидал бы, что здесь будет работать автовификация:

my $hash_ref = $hash_for{$key_level_1};
$hash_ref->{$key_level_2} = $value;

как здесь:

$hash_for{$key_level_1}{$key_level_2} = $value;

Магия, вероятно, не работает как оптимизация. И оптимизирующий компилятор заметил бы, что a = a + 1 - это то же самое, что и a++, так что если бы на языке ассемблера был оператор приращения, он мог бы использовать эту оптимизированную инструкцию вместо того, чтобы делать вид, что ему нужно сохранить первое значение, а затем перезаписывать это потому, что это на самом деле не нужно.

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

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