Comparing empty string with number - perl

I have a global value:
my $gPrevious ='';
# main();
func();
sub func {
my $localval = 52552;
if ($gPrevious != $localval) {
------------
x statements;
}
}
Output:
Argument "" isn't numeric in numeric ne (!=) at line x.
Why do I use the ne operator?
Here I am comparing with an empty string.

The value undef is there specifically so that you can test whether a variable has been defined yet. Initialising a variable to a string when it is to be compared to a number rather defeats this purpose.
You should leave your $gPrevious variable undefined, and test for that inside your subroutine.
Like this
my $gPrevious;
func();
sub func {
my $localval = 52552;
unless (defined $gPrevious and $gPrevious == $localval) {
# x statements;
}
}

Using the '==' or '!=' comparison operator requires having two numbers.
If one of the two is a string that perl can easily make a number such as "1", perl will do what you want and compare the 1.
But: If you compare a string or undef with the '!=' operator, there will be a warning.
In your case, I suggest you simply use
$gPrevious = 0;
# ...
if ( $gPrevious != $local_val ){ #...
which would get rid of the warning and work fine.
Testing for defined is another possibility, while testing for integers is not trivial.
You could use eq instead, but this is problematic, as then for example '1.0' ne '1'.

Perl provides two separate operators for string comparison (eq/ne) and numeric comparison (==/!=). LHS value must match the type of operator being used. In your case, LHS is a string and you are using a numeric operator for comparison. Hence, the error.

Related

What is the definition of stringwise equality in Perl?

I was curious about the results of the following perl snippets:
my $var1 ;
my $var2 ;
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
and
my $var1 ;
my $var2 = "";
if( $var1 eq $var2 ) {
print "yes";
} else {
print "no";
}
They turn out to be yes(Perl 5.16).
Unlike javascript specificaton, there is clear description of Equality Comparison Algorithm(http://www.ecma-international.org/ecma-262/5.1/#sec-11.9.3), the perl doc for Equality Operators says:
Binary "eq" returns true if the left argument is stringwise equal to the right argument.
But what is the definition of stringwise equality?
You're not having a problem with the definition of stringwise equality. What you don't seem to have wrapped your head arround yet is the concept of stringification. In this particular case, undef stringifies to ''.
Perl has monomorphic operators (mostly) and contextually polymorphic data types.
What this means is that it is the operator that dictates how the data is represented. The eq operator is for stringwise equality. The == operator is for numeric equality. When == is applied to strings, they are treated as numbers. When eq is applied to numbers, they are treated as strings. Each of these internal transformations follow a very specific set of rules.
Thus, when you apply a stringwise operator to a pair of values, those values will be treated as strings. When you apply a numeric operator to a pair of values, they will be treated as numbers.
In your second example, $var1 contains undef, and $var2 contains an empty string. When you compare $var1 eq $var2, the rules of stringification are applied to the operands. $var2 is already a string. But $var1 must be stringified before it can be compared. The rule of stringification for an undefined value is to treat it as an empty string. So $var1 eq $var2 is seen by the eq operator as '' eq '', which is to say, empty string eq empty string.
TRUE.
Note: In modern versions of Perl, using an undefined scalar variable in a stringwise operation only results in stringification for the duration of the operation; the underlying data in the container isn't altered. (see perldata).
perldata is one resource on this topic.
Your definition question has been answered by DavidO, but the (probably) most important piece of information is in TLP's comment - you should always use these two at the top of every script:
use strict;
use warnings;
The one relevant in this case is use warnings, which will produce the following warning:
Use of uninitialized value $var1 in string eq
Some programs go even further - this will cause the program to die because of this:
use warnings FATAL => 'all';
You are comparing variables using eq, which means you are doing a string comparison. A string comparison expects two strings, but you are providing an undefined variable (variable declared, but not defined, so not a string). This is not clean because you are using that operator with invalid input. If you wanted to know if the variables differ, not just the strings they (may or may not) represent, you would not use a string comparison like that.
It works because Perl knows that you want to compare strings (eq) so it assumes you don't care about the fact that a variable has no value assigned. The undefined variable is converted into an empty string (temporarily), which is then compared. (Actually, the variable itself isn't converted, it's still undef after the comparison, but that's not important.)
Of course, this assumption could be wrong and there might be a bug in your code. So it'd be better to check for invalid input before comparing it.
You don't care about the difference between undef and '' (and you know why).
You could explicitly compare empty strings instead of undef. Another programmer who is reading your code will know what's going on (won't assume there's a bug).
if (($var1 // q()) eq ($var2 // q()))
...
In many cases, you might actually care about undef.
For example, your script might take some input (maybe a hash) and if that input variable is an empty string, that would be okay, but if it's undef (maybe not found in the input hash), that would be an error.
if (!defined($var1))
{
die "Input data missing, can't continue!";
}
if ($var1 eq $var2)
...

Perl character searching in a string with patterns

Is there a built in function on which determines if a character is present in a string. Also how can you determine if a value is a string or a number.
There is a built in function in perl called the index function also use pattern matching like
to use index : index($stringvariable,"char to search");
to determine if a number use the code m/\d/
if you want to determine if a value is a string use m/\D/
Use pattern matching techniques.
Perl's scalars are a string and a number at the same time. To test whether a scalar can be used as a number without any warnings:
use Scalar::Util qw/looks_like_number/;
my $variable = ...;
if (not defined $variable) {
# it is not usable as either a number or a string, as it is "undef"
}
elsif (looks_like_number $variable) {
# it is a number, but can also be used as a string
}
else {
# you can use it as a string
}
Actually, the story is a bit more complex with objects which may or may not be usable as numbers or strings. Furthermore, looks_like_number can return a true value for Infinity and NaN (not a number), which may not be what you consider to be a number.
To test whether a string contains some substring, you can use regexes or the index function:
my $haystack = "foo";
my $needle = "o";
if (0 <= index $haystack, $needle) {
# the $haystack contains the $needle
}
Some people prefer the equivalent test -1 != index ... instead.

Perl thinks all strings are equal

Edit: I did try using eq instead of == earlier, and it did not work. For some reason now it does. It's possible that there was another error at that point which prevented it from working, but now this has been resolved. Thank you.
I'm trying to do some simple validation. I want to make sure the redirect url being fed through a variable begins with a particular site or not. If it does, the redirect goes through, if not, it redirects to the root of the site. Seems pretty straight forward, right?
$redir = $input{'redirect'};
$redir_sub = substr($redir, 0, 21);
if ($redir_sub == "http://www.mysite.com") {
print "Location: $redir \n\n";
}else{
print "Location: http://www.mysite.com \n\n";
}
The thing is, no matter what variable I place in there, it the "if" returns as true. I could put my favorite webcomic in there and it'll redirect to it, despite the string not matching. For example this:
$redir = $input{'redirect'};
$redir_sub = "http://www.yahoo.com"
if ($redir_sub == "http://www.mysite.com") {
print "Location: $redir_sub \n\n";
}else{
print "Location: http://www.mysite.com \n\n";
}
That redirects to yahoo! What is going on?
if ($redir_sub == "http://www.mysite.com")
should be
if ($redir_sub eq "http://www.mysite.com")
as eq is string equality operator, and using == forces number comparison so in this case condition always evaluates to trues as 0 == 0 is true.
Operator == is used to compare numbers. You should replace it with operator eq
TL;DR: use eq not ==!
Perl as a language seems to have a problem: It uses the same data type (the scalar) for a lot of different things, including strings and numbers. A scalar is both at the same time, not just one of those. Perl has no type annotations, and there is no way to indicate if a variable holds a string or a number.
This produces problems when we consider equality tests. Assuming two scalars $a = "42.0" and $b = 42. Are they equal? Yes and no:
The strings "42" and "42.0" are not the same thing! These are not equal.
The numbers 42 and 42.0 are equal!
As indicated above, Perl does not use a type system to solve this ambiguity. Rather, it uses different sets of operators for string and numeric operations:
eq ne lt le gt ge cmp
== != < <= > >= <=>
Your problem is that you are not using
use warnings;
Which is why Perl is allowing you to make this mistake. You are using the numeric equality operator to compare strings. Hence, Perl first tries to convert each parameter to a number. And since your strings do not begin with numbers (and do not look like numbers), they are converted to zero 0. Hence your expression
if ($redir_sub == "http://www.mysite.com")
Really means this
if (0 == 0)
Which of course always returns true.
If you had been using warnings, you would have gotten the errors:
Argument "http..." isn't numeric in numeric eq (==) at ...
Argument "http..." isn't numeric in numeric eq (==) at ...
Which would have been a hint as to your problem that you should be using eq and not == to compare strings.
== does numeric comparison. eq does string comparison. When you use a string as a number, perl passes your string through your c library's aton(). So you're really asking your computer if 0 == 0 which is true.

Warnings on equality operators

Has something changed in Perl or has it always been this way, that examples like the second ($number eq 'a') don't throw a warning?
#!/usr/bin/env perl
use warnings;
use 5.12.0;
my $string = 'l';
if ($string == 0) {};
my $number = 1;
if ($number eq 'a') {};
# Argument "l" isn't numeric in numeric eq (==) at ./perl.pl line 6.
Perl will be try to convert a scalar to the type required by the context where it is used.
There is a valid conversion from any scalar type to a string, so this is always done silently.
Conversion to a number is also done silently if the string passes a looks_like_number test (accessible through Scalar::Util). Otherwise a warning is raised and a 'best guess' approximation is done anyway.
my $string = '9';
if ( $string == 9 ) { print "YES" };
Converts the string silently to integer 9, the test succeeds and YES is printed.
my $string = '9,8';
if ( $string == 9 ) { print "YES" };
Raises the warning Argument "9,8" isn't numeric in numeric eq (==), converts the string to integer 9, the test succeeds and YES is printed.
To my knowledge it has always been this way, at least since v5.0.
It has been that way.
In the first if, l is considered to be in numeric context. However, l cannot be converted to a number. Therefore, a warning is emitted.
In the second if, the number 1 is considered to be in string context. Therefore the number 1 is converted to the string '1' before comparison and hence no warnings are emitted.
Did you use a lowercase "L" on purpose? It's often hard to tell the difference between a lowercase "L" and one. You would have answered your own question if you had used a one instead.
>perl -wE"say '1' == 0;"
>perl -wE"say 1 eq 'a';"
>
As you can see,
If one needs a number, Perl will convert a string to a number without warning.
If one needs a string, Perl will convert a number to a string without warning.
Very consistent.
You get a warning when you try to convert a lowercase L to a number, but how is that surprising?

Why does !1 give me nothing in Perl?

This is strange.
The following:
$sum = !0;
print $sum;
prints out 1 as you would expect. But this
$sum = !1;
print $sum;
prints out nothing. Why?
Be careful: what you've written isn't doing what you think it's doing. Remember, perl has no real boolean datatype. It's got scalars, hashes, lists, and references. The way it handles true/false values, then, is contextual. Everything evaluates to "true" in perl except for undefined variables, the empty list, the empty string, and the number 0.
What your code is doing, then, is taking the inverse of a value that evaluates to "false", which can be anything which is not in the list above. By convention and for simplicity's sake, perl returns 1 (though you should not rely on that; it could very well return a list containing a series of random numbers, because that will evaluate to "true" as well.)
A similar thing happens when you ask for the inverse of a value that evaluates to "true." What's actually being printed out is not "nothing," it's the empty string (''), which, as I mentioned, evaluates to "false" in boolean expressions. You can check this:
print "This evaluates to false\n" if( (!1) eq '');
If you're asking for why perl spits out the empty string instead of one of the other "false" values, well, it's probably because perl is made to handle strings and that's a perfectly reasonable string to hand back.
The operators that only return a boolean result will always return 1 for true and a special false value that's "" in string contexts but 0 in numeric contexts.
Here's an addendum to the other great answers you've already gotten.
Not's Not Not
Consider the following code that tests each of Perl's 'not' operators:
#!/usr/bin/perl
use strict;
use warnings;
for( '!1', 'not 1', '~0' ) {
my $value = eval;
my $zero_plus = 0 + $value;
print join "\n",
"\nExpression: $_",
"Value: '$value'",
"Defined: " . defined $value,
"Length: " . length($value),
"Plus: " . +$value,
"Plus Zero: '$zero_plus'",
'';
}
print "\nTest addition for a literal null string: ";
print 0+'', "\n";
use Scalar::Util qw(dualvar);
{ # Test a dualvar
my $value = dualvar 0, '';
my $zero_plus = 0+$value;
print join "\n",
"\nExpression: dualvar",
"Value: '$value'",
"Defined: " . defined $value,
"Length: " . length($value),
"Plus: " . +$value,
"Plus Zero: '$zero_plus'",
'';
}
Executing it results in the following. Notice the warning message:
Argument "" isn't numeric in addition (+) at test.pl line 21.
Expression: !1
Value: ''
Defined: 1
Length: 0
Plus:
Plus Zero: '0'
Expression: not 1
Value: ''
Defined: 1
Length: 0
Plus:
Plus Zero: '0'
Expression: ~0
Value: '4294967295'
Defined: 1
Length: 10
Plus: 4294967295
Plus Zero: '4294967295'
Test addition for a literal null string: 0
Expression: dualvar
Value: ''
Defined: 1
Length: 0
Plus:
Plus Zero: '0'
From this we learn several things.
The first two items are not all that exciting:
!1 and not 1 behave in basically the same way.
Unsurpisingly, ~1 is different (it's the bitwise not).
Now, the interesting item:
While we do get a warning for line 21 (0+''), there is no warning generated when we add 0+!1.
It Takes Two to Tangle
Something fishy is happening, and that fishiness has to do with special scalar contexts in Perl. In this case, the distinction between numeric and string contexts. And the ability to create a variable that has different values in each context, aka a dual variable.
It looks like !1 returns a dual variable that returns 0 in numeric context and the null string in string context.
The dualvar test at the end shows that a homemade dualvar works the same way as !1.
But It's A Good Thing
Like many Perl features, dual variables seem at first to defy expectations, and can be confusing. However, like those other features, used appropriately they make life much easier.
As far as I know, a dualvar of 0 and '' is the only defined value that will return false in all scalar contexts. So it is a very sensible return value for !1. One could argue that undef is a good false result, but then an uninitialized variable is not distinguishable from a false value. Also, attempts to print or add the results of booleans would then be plagued with unnecessary warnings.
Another famous dualvar is $! or $OS_ERROR if you use English. In numeric form, you get the error code, in string form, the error code is translated for you.
It's Not Nothing, It's Empty, But It's Nought
So in summary, you aren't getting nothing, you aren't getting an empty string, and you aren't getting zero.
You are getting a variable that is both an empty string and 0 at the same time.
The ! operator does boolean operations. "" (The empty string) is just as false as 0 is. 1 is a convenient true value. ! shouldn't be relied on to do anything other than produce some true/false value. Relying on the exact value beyond that is dangerous and may change between versions of Perl.
See perldoc perlsyn:
Truth and Falsehood
The number 0, the strings '0' and '' ,
the empty list () , and undef are all
false in a boolean context. All other
values are true. Negation of a true
value by ! or not returns a special
false value. When evaluated as a
string it is treated as '' , but as a
number, it is treated as 0.
There, if you print the value as a number, you will get 0 rather than the empty string:
printf "%d\n", $_ for map { !$_ } (1, 0);
or
print 0 + $_, "\n" for map { !$_ } (1, 0);
Compare those to
printf "%s\n", $_ for map { !$_ } (1, 0);
and
print $_, "\n" for map { !$_ } (1, 0);