String comparison operator trouble

J

J.D. Baldwin

I solved my problem by rewriting, but I'm wondering why I am getting errors
with respect to 'ne' and 'eq' operators.

This code works fine (Perl 5.8.8):

$ cat minimal.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( $item ne $VAL );

print "$item\n";
}
$ ./minimal.pl
xyz
xyz


But if I add a test for 'undef' I get warnings:

$ cat ./minimal2.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( undef $item ) or ( $item ne $VAL );

print "$item\n";
}
$ ./minimal2.pl
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.

Even weirder if I try 'eq':

$ cat ./minimal3.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( undef $item ) or ( $item eq $VAL );

print "$item\n";
}
$ ./minimal3.pl
Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Use of uninitialized value in string eq at ./minimal3.pl line 12.
Use of uninitialized value in concatenation (.) or string at ./minimal3.pl line 14.

Can anyone explain to me why I see this behavior? It seems like a
perfectly normal chained test.
 
J

Jens Thoms Toerring

J.D. Baldwin said:
I solved my problem by rewriting, but I'm wondering why I am getting errors
with respect to 'ne' and 'eq' operators.
This code works fine (Perl 5.8.8):
$ cat minimal.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( $item ne $VAL );

print "$item\n";
}
$ ./minimal.pl
xyz
xyz

But if I add a test for 'undef' I get warnings:
$ cat ./minimal2.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( undef $item ) or ( $item ne $VAL );

'undef' is an unary operator that undefines what it's applied to,
not a test if the variable is undefined. I guess you meant to use

next if ! defined $item || $item ne $VAL;
or
next unless defined $item && $item eq $val;

Regards, Jens
 
S

szr

J.D. Baldwin said:
I solved my problem by rewriting, but I'm wondering why I am getting
errors
with respect to 'ne' and 'eq' operators.
[...]

But if I add a test for 'undef' I get warnings:

$ cat ./minimal2.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( undef $item ) or ( $item ne $VAL );

print "$item\n";
}
$ ./minimal2.pl
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.
Use of uninitialized value in string ne at ./minimal2.pl line 12.

The problem is you're calling undef $item which undefines $item.

What you want to use instead is:

next if ( ! defined $item ) or ( $item ne $VAL );


defined() will tell you if a scalar has been defined or so. For arrays
and hashes, drop the define, as if(@array) and if(%hash) will return
true only if there are items within.

Also see: perldoc -f defined and also: perldoc -f undef
This will help you learn what undef() is used for and about using
defined().

How this helps.
 
D

Dave B

J.D. Baldwin said:
next if ( undef $item )

$item == undef

or

!defined($item)

"perldoc -f undef" to see why your command does not do what you want, and
the warnings you get are expected.
 
J

Jim Gibson

J.D. Baldwin said:
I solved my problem by rewriting, but I'm wondering why I am getting errors
with respect to 'ne' and 'eq' operators.
But if I add a test for 'undef' I get warnings:

$ cat ./minimal2.pl
#!/opt/gnu/bin/perl

use warnings;
use strict;

my $VAL = 'xyz';

my @foo = ( 'abc', 'def', 'ghi', 'xyz', 'jkl', 'xyz', 'uvw' );

foreach my $item ( @foo )
{
next if ( undef $item ) or ( $item ne $VAL );

print "$item\n";
}

The test for defined-ness is defined, not undef. The operator undef
sets the variable to undef.

perldoc -f defined
 
J

John W. Krahn

Dave said:
$item == undef

That won't work correctly either.

$ perl -le'
for my $item ( 0, 1, "two" ) {
print $item if $item == undef;
}
'
0
two


John
 
U

Uri Guttman

DB> $item == undef

very wrong. the only way to test for definedness is with defined. wipe
that idiom out of your brain. just to make it clear, try that with any
false value in $item (and enable warnings too for more results).

uri
 
J

Jürgen Exner

Dave B said:
$item == undef

This doesn't do what you seem to think it is doing.

First of all it will trigger two warnings (for generic $item):
- Use of uninitialized value in numeric eq (==) [*]
- Argument "...." isn't numeric in numeric eq (==) [**]

[*]: because undef is not initialized, surprise, surprise
[**]: because in the generic case $item may contain a string or -gasp-
be undefined instead of number.

And second the numerical value of undef is 0 and therefore this
comparison will yield true whenever the numerical value of $item is 0,
in particular also when $item contains a string that does not start with
a number.

jue
 
J

J.D. Baldwin

In the previous article said:
'undef' is an unary operator that undefines what it's applied to,
not a test if the variable is undefined. I guess you meant to use

<slaps forehead>

So much for focusing on the comparison operator.

Thank you (and you others) for setting me straight on this.

undef ne ( ! defined ). Got it.
 
D

Dave B

Jürgen Exner said:
This doesn't do what you seem to think it is doing.

Thank you, John and Uri for correcting me on this. It seemed to work in a
quick and simple test like perl -e 'print "ok" if $var == undef;' but, as
you correctly point out, that idiom cannot be used in general.

Thanks
 
U

Uri Guttman

DB> Thank you, John and Uri for correcting me on this. It seemed to work in a
DB> quick and simple test like perl -e 'print "ok" if $var == undef;' but, as
DB> you correctly point out, that idiom cannot be used in general.

in general or at all. it fails for too many scalar values (as someone
else said, any string that starts with a non-digit will fail among
others). so don't call it an idiom, it is a bug.

uri
 
S

szr

Jürgen Exner said:
This doesn't do what you seem to think it is doing.

Indeed, == forces a numeric context, so undef becomes 0. using 'eq'
interestingly seems to work (though, still with warnings - see below.)
First of all it will trigger two warnings (for generic $item):
- Use of uninitialized value in numeric eq (==) [*]
- Argument "...." isn't numeric in numeric eq (==) [**]


[*]: because undef is not initialized, surprise, surprise
[**]: because in the generic case $item may contain a string or -gasp-
be undefined instead of number.

I get a different set of warnings (using both 5.10.0 and 5.8.8)

$ perl5.8.8 -Mstrict -Mwarnings -we 'my $x = 1; print +($x == undef ?
"[undef]" : "[$x]"), "\n";'
Warning: Use of "undef" without parentheses is ambiguous at -e line 1.
Search pattern not terminated or ternary operator parsed as search
pattern at -e line 1.

I'm not sure where it's getting "Search pattern from, but I'm guessing
this has something to do with using (if ? then : else) instead of
if{}else{} ?


$ perl5.8.8 -Mstrict -Mwarnings -we 'my $x = 1; print +($x eq (undef)
? "[undef]" : "[$x]"), "\n";'
Use of uninitialized value in string eq at -e line 1.
[1]

$ perl5.8.8 -Mstrict -Mwarnings -we 'my $x = 0; print +($x eq (undef)
? "[undef]" : "[$x]"), "\n";'
Use of uninitialized value in string eq at -e line 1.
[0]

$ -Mstrict -Mwarnings -we 'my $x = undef; print +($x eq (undef) ?
"[undef]" : "[$x]"), "\n";'
Use of uninitialized value in string eq at -e line 1.
Use of uninitialized value in string eq at -e line 1.
[undef]

Of course, I wouldn't use this in any real code, and one should always
use defined($scalar) to test for defininty.
 
U

Uri Guttman

s> Indeed, == forces a numeric context, so undef becomes 0. using 'eq'
s> interestingly seems to work (though, still with warnings - see below.)

what do you mean seems to work? try '' eq undef. the rule is simple,
don't use undef in any comparisons. that is why defined is NEEDED. it is
the only way to test for undef (which is out of band data for all other
ops).

s> Of course, I wouldn't use this in any real code, and one should always
s> use defined($scalar) to test for defininty.

then don't say things like it seems to work for eq when you know it
doesn't.

uri
 
S

szr

Uri said:
s> Indeed, == forces a numeric context, so undef becomes 0. using
s> 'eq' interestingly seems to work (though, still with warnings -
s> see below.)

what do you mean seems to work? try '' eq undef. the rule is simple,
don't use undef in any comparisons. that is why defined is NEEDED. it
is the only way to test for undef (which is out of band data for all
other ops).

s> Of course, I wouldn't use this in any real code, and one should
always s> use defined($scalar) to test for defininty.

then don't say things like it seems to work for eq when you know it
doesn't.

No need to get upset. "Seems to work" only referred to having to
appearance to work as expected within the limit of those test cases. I
never said it's something that should be used, and in fact, I pointed to
the exact opposite, to not use such a construct and instead use
defined($scalar)
 
H

Hans Mulder

I get a different set of warnings (using both 5.10.0 and 5.8.8)

$ perl5.8.8 -Mstrict -Mwarnings -we 'my $x = 1; print +($x == undef ?
"[undef]" : "[$x]"), "\n";'
Warning: Use of "undef" without parentheses is ambiguous at -e line 1.
Search pattern not terminated or ternary operator parsed as search
pattern at -e line 1.

I'm not sure where it's getting "Search pattern from, but I'm guessing
this has something to do with using (if ? then : else) instead of
if{}else{} ?

The "undef" operator takes an argument if it can find one.
If the first non-whitespace character after "undef" is a "?",
then the parser assumes this is a ??-style pattern match.

In this case, there is no closing "?". If there were one, then
you'd get a message that "undef" wants an lvalue argument and
pattern matches are not lvalues.

As the other warning points out, you can avoid this kind of mis-
parse by writing "undef()".

In theory you can leave out the parentheses if the next character
is a one that cannot be the first in an expression (such as ";").
This only works if you know about obscure kinds of expressions
(such as *globs and ??-patterns).

For example, you might think that "undef * 5" looks like multiplication,
but perl interprets this as undef(*5). As it happens, *5 is an lvalue,
so this will "work" (for some definition of "work").

Don't try this at home, kids.

-- HansM
 
S

szr

Hans said:
I get a different set of warnings (using both 5.10.0 and 5.8.8)

$ perl5.8.8 -Mstrict -Mwarnings -we 'my $x = 1; print +($x ==
undef ? "[undef]" : "[$x]"), "\n";'
Warning: Use of "undef" without parentheses is ambiguous at -e
line 1. Search pattern not terminated or ternary operator parsed
as search pattern at -e line 1.

I'm not sure where it's getting "Search pattern from, but I'm
guessing this has something to do with using (if ? then : else)
instead of if{}else{} ?

The "undef" operator takes an argument if it can find one.
If the first non-whitespace character after "undef" is a "?",
then the parser assumes this is a ??-style pattern match.

In this case, there is no closing "?". If there were one, then
you'd get a message that "undef" wants an lvalue argument and
pattern matches are not lvalues.

Ah I didn't realize that, thank you.
As the other warning points out, you can avoid this kind of mis-
parse by writing "undef()".

In theory you can leave out the parentheses if the next character
is a one that cannot be the first in an expression (such as ";").
This only works if you know about obscure kinds of expressions
(such as *globs and ??-patterns).

Seems being explicit is once again the safer way.
For example, you might think that "undef * 5" looks like
multiplication, but perl interprets this as undef(*5). As it
happens, *5 is an lvalue, so this will "work" (for some definition of
"work").

This is true. Many people seem to be confused as to just how to use
undef(), as some see it as a (none) value to be assigned to return a
scalar to an undefined state, rather than as a function and knowing
exactly what it does.


Thank you for taking the time to reply.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads


Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,810
Latest member
Kassie0918

Latest Threads

Top