Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is the error "Use of uninitialized value in string ne" coming from?

Tags:

perl

Where is the uninitialised value in the below code?

#!/usr/bin/perl
use warnings;

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

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

Output:

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.
like image 861
11684 Avatar asked Jan 07 '12 14:01

11684


2 Answers

You can't use undef in a string comparison without a warning.

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

will raise a warning. If you want to test if a variable is defined or not, use:

if (defined $var) { ... }

Comments about the original question:

That's a strange way to iterate over an array. The more usual way of doing this would be:

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

and drop the $sitecount variable completely, and don't overwrite $url in the loop body. Also drop the undef value in that array. If you don't want to remove that undef for some reason (or expect undefined values to be inserted in there), you could do:

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

If you do want to test for undefined with your form of loop construct, you'd need:

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

to avoid the warnings, but beware of autovivification, and that loop would stop short if you have undefs mixed in between other live values.

like image 141
Mat Avatar answered Sep 22 '22 22:09

Mat


The correct answers have already been given (defined is how you check a value for definedness), but I wanted to add something.

In perlop you will read this description of ne:

Binary "ne" returns true if the left argument is stringwise not equal to the right argument.

Note the use of "stringwise". It basically means that just like with other operators, such as ==, where the argument type is pre-defined, any arguments to ne will effectively be converted to strings before the operation is performed. This is to accommodate operations such as:

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

Perl has no fixed data types, and relies on conversion of data. In this case, undef (which coincidentally is not a value, it is a function: undef(), which returns the undefined value), is converted to a string. This conversion will cause false positives, that may be hard to detect if warnings is not in effect.

Consider:

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

This will print "yes", even though clearly the empty string "" is not equal to not defined. By using warnings, we can catch this error.

What you want is probably something like:

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

Or, if you want to skip to a certain array element:

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

Same basic principle, but using an array slice, and avoiding indexes:

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

Note that the use of last instead of next is the logical equivalent of your while loop condition: When an undefined value is encountered, the loop is exited.

More debugging: http://codepad.org/Nb5IwX0Q

If you, like in this paste above, print out the iteration counter and the value, you will quite clearly see when the different warnings appear. You get one warning for the first comparison "a" ne undef, one for the second, and two for the last. The last warnings come when $sitecount exceeds the max index of @sites, and you are comparing two undefined values with ne.

like image 42
TLP Avatar answered Sep 24 '22 22:09

TLP