Откуда возникает ошибка «Использование неинициализированного значения в строке ne»? - PullRequest
10 голосов
/ 07 января 2012

Где находится неинициализированное значение в приведенном ниже коде ?

#!/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.

Ответы [ 4 ]

20 голосов
/ 07 января 2012

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

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

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

if (defined $var) { ... }

Комментарии к оригинальному вопросу:

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

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

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

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

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

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

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

6 голосов
/ 07 января 2012

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

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

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

Обратите внимание на использование «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(), которая возвращает неопределенное значение) преобразуется в строку. Это преобразование вызовет ложные срабатывания, которые могут быть трудно обнаружить, если 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: Когда встречается неопределенное значение, цикл завершается.

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

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

5 голосов
/ 07 января 2012

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

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

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

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

defined $something
3 голосов
/ 07 января 2012

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 более условно, как Мэт исследует в своем ответе.

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