Где происходит ошибка "Использование неинициализированного значения в строке ne"?

Где нетинициализированное значение в ниже код?

#!/usr/bin/perl
use warnings;

my @sites = (undef, "a", "b");
my $sitecount = 1;
my $url;

while (($url = $sites[$sitecount]) ne undef) {
   $sitecount++;
}

Вывод:

Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.
Use of uninitialized value in string ne at t.pl line 6.

Ответ 1

Вы не можете использовать undef в сравнении строк без предупреждения.

if ("a" ne undef) { ... }

поднимет предупреждение. Если вы хотите проверить, определена ли переменная или нет, используйте:

if (defined $var) { ... }

Комментарии об исходном вопросе:

Это странный способ перебора массива. Более обычный способ сделать это:

foreach my $url (@sites) { ... }

и полностью отбросить переменную $sitecount и не перезаписывать $url в теле цикла. Также оставьте значение undef в этом массиве. Если вы по какой-либо причине не хотите удалять этот undef (или ожидаете, что там будут вставлены значения undefined), вы можете сделать:

foreach my $url (@sites) {
  next unless defined $url;
  ...
}

Если вы хотите протестировать undefined с помощью своей формы цикла, вам понадобится:

while (defined $sites[$sitecount]) {
  my $url = $sites[$sitecount];
  ...
  $sitecount++;
}

чтобы избежать предупреждений, но будьте осторожны с автовивификацией, и этот цикл прекратится, если у вас есть undef, смешанный между другими значениями live.

Ответ 2

Правильные ответы уже даны (defined - это то, как вы проверяете значение для определения), но я хотел что-то добавить.

В perlop вы прочтете это описание ne:

Двоичный "ne" возвращает true, если левый аргумент не равен к правильному аргументу.

Обратите внимание на использование "stringwise". В основном это означает, что как и для других операторов, таких как ==, где тип аргумента предварительно определен, любые аргументы ne будут эффективно преобразованы в строки перед выполнением операции. Это должно учитывать такие операции, как:

if ($foo == "1002")  # string "1002" is converted to a number
if ($foo eq 1002)    # number 1002 is converted to a string

Perl не имеет фиксированных типов данных и полагается на преобразование данных. В этом случае undef (который по совпадению не является значением, это функция: undef(), которая возвращает значение undefined), преобразуется в строку. Это преобразование вызовет ложные срабатывания, которые могут быть трудно обнаружить, если warnings не действует.

Рассмотрим:

perl -e 'print "" eq undef() ? "yes" : "no"'

Это будет печатать "да", хотя ясно, что пустая строка "" не равна not defined. Используя warnings, мы можем поймать эту ошибку.

Что вы хотите, вероятно, что-то вроде:

for my $url (@sites) {
    last unless defined $url;
    ...
}

Или, если вы хотите перейти к определенному элементу массива:

my $start = 1;
for my $index ($start .. $#sites) {
   last unless defined $sites[$index];
   ...
}

Одинаковый базовый принцип, но используя срез массива и избегая индексов:

my $start = 1;
for my $url (@sites[$start .. $#sites]) {
    last unless defined $url;
    ...
}

Обратите внимание, что использование last вместо next является логическим эквивалентом вашего цикла while: Когда встречается значение undefined, цикл завершается.

Больше отладки: http://codepad.org/Nb5IwX0Q

Если вы, как и в этом пасте выше, распечатываете счетчик итераций и значение, вы будете совершенно ясно видеть, когда появляются разные предупреждения. Вы получаете одно предупреждение для первого сравнения "a" ne undef, второе для второго и два для последнего. Последние предупреждения появляются, когда $sitecount превышает максимальный индекс @sites, и вы сравниваете два значения undefined с ne.

Ответ 3

Возможно, сообщение было бы лучше понять, если бы это было:

You are trying to compare an uninitialized value with a string.

Неинициализированное значение, конечно, undef.

Чтобы явно проверить, существует ли $something, вам нужно написать

defined $something

Ответ 4

ne предназначен для сравнения строк, а undef не является строкой:

#!/usr/bin/perl
use warnings;
('l' ne undef) ? 0 : 0;

Использование неинициализированного значения в строке ne в строке t.pl 3.

Это работает, но вы получаете предупреждение [немного запутанное] (по крайней мере, с use warnings), потому что undef не является "инициализированным значением" для ne.


Вместо используйте оператор defined, чтобы определить, определено ли значение:

#!/usr/bin/perl
use warnings;

my @sites = (undef, "a", "b");
my $sitecount = 1;
my $url;

while (defined $sites[$sitecount]) {   # <----------
   $url = $sites[$sitecount];
   # ...
   $sitecount++;
}

... или цикл над массивом @sites более традиционно, как анализирует Мат в своем ответе.