In perl, one should compare two strings with "eq" or "ne" etc.
I am a little surprised the following code snippet will print "yes".
$str = "aJohn";
$x = substr($str, 1);
if ($x == "John") {
print "yes\n";
}
My perl has version v5.18.4 on Ubuntu.
Is there a case where the "==" on two strings produce a different result from "eq"?
Thanks.
"foo" == "bar" is true. "foo" eq "bar" is false.
The reason for this: == is numeric comparison. "foo" and "bar" are both numerically evaluate to 0 (like "17foo" evaluates numerically to 17); since 0 == 0, "foo" == "bar". This is not normally the operation you are looking for.
Related
Is it true that Perl numerically evaluates any string that starts with a number as numerically equivalent to that number (after warning that it is not numerical)?
I am trying to understand how the various comparison operators work.
I wrote a script to output various TRUE or FALSE results as follows. (My question is posed afterward.)
(For my post I have condensed the curly braces into single lines, to save space and maybe make it easier to read.
This is my first question to StackOverflow, so if I can improve how my question was asked, or where to ask it, please let me know.)
#!/usr/bin/perl -w
#compOpers-mathyIn_TF.pl
use strict;
print "Enter expression to compare.\n";
chomp ( my $var = <STDIN> );
if ( $var == 0 ) { print "$var == 0 is TRUE.\n"; }
else { print "$var == 0 is FALSE.\n"; }
if ( $var == 1 ) { print "$var == 1 is TRUE.\n"; }
else { print "$var == 1 is FALSE.\n"; }
if ( $var eq "0" ) { print "$var eq \"0\" is TRUE.\n"; }
else { print "$var eq \"0\" is FALSE.\n"; }
if ( $var eq "1" ) { print "$var eq \"1\" is TRUE.\n"; }
else { print "$var eq \"1\" is FALSE.\n"; }
I tried this with some mathematical expressions such as "4 - 3" (and "4-3"), which didn't work. Sample output:
~\Perlscripts>perl compOpers_mathyInput_TF.pl
Enter expression to compare.
4 - 3
Argument "4 - 3" isn't numeric in numeric eq (==) at compOpers_mathyInput_TF.pl line 8, <STDIN> line 1.
4 - 3 == 0 is FALSE.
4 - 3 == 1 is FALSE.
4 - 3 eq "0" is FALSE.
4 - 3 eq "1" is FALSE.
~\Perlscripts>
After some research I realized that Perl will not mathematically parse this STDIN by default;
once I added an eval function to the code as follows (done for each if block), it worked better.
if ( eval($var) == 0 ) { print "eval($var) == 0 is TRUE\.\n"; }
else { print "eval($var) == 0 is FALSE\.\n"; }
But before getting to that point, I had already received some puzzling results with my original code.
At first I thought the below result was mathematically parsing my input of "1 x 1", but that doesn't actually seem to be the case.
~\Perlscripts>perl compOpers_mathyInput_TF.pl
Enter expression to compare.
1 x 1
Argument "1 x 1" isn't numeric in numeric eq (==) at compOpers_mathyInput_TF.pl line 8, <STDIN> line 1.
1 x 1 == 0 is FALSE.
1 x 1 == 1 is TRUE. <<<-----
1 x 1 eq "0" is FALSE.
1 x 1 eq "1" is FALSE.
~\Perlscripts>
Here are other similar results where Perl is evaluating the second truth test in the script ( == 1 ) test to be TRUE, even though they are clearly not equal (along with a couple FALSE results too):
1 x 1 == 1 is TRUE.<br>
1/235 == 1 is TRUE.<br>
1x2 == 1 is TRUE.<br>
2x1 == 1 is FALSE.<br>
1andsometimes7 == 1 is TRUE.<br>
7isnt1 == 1 is FALSE.<br>
Question 1)
From what I can see, Perl numerically evaluates any string that starts with a number as numerically equivalent to that number (after warning that it is not numerical).
Is this true? Is it a problem? Why does it work this way?
(I also tried setting the second truth test to match a different number than "1" (I used 8), and the results were the same.)
I also have a follow up question, a result that was somewhat surprising to me. Once I changed the script to use eval, the numerical comparisons worked as expected. But the string comparison also came up true here:
~\Perlscripts>perl compOpers_eval-mathyInput_TF.pl
Enter first expression to compare.
4-3
eval(4-3) == 0 is FALSE.
eval(4-3) == 1 is TRUE.
eval(4-3) eq "0" is FALSE.
eval(4-3) eq "1" is TRUE. <<<-----
~\Perlscripts>
**Question 2**
(mainly just asking for confirmation)
`"4-3"` is obviously not the same characters as in the string `"1"`. Am I correct that the `eval()` parses my input, evaluates it mathematically, and the result is then essentially placed into a string, `"1"`, which is exactly the same characters as the comparison string `"1"`?
Perl's a bit weird because it's the operators that coerce the data rather than the data deciding what the operators do. Since Perl has a scalar instead of a more specific data type, it figures out if you wants strings or numbers based on what you are doing. If you use numeric operators, Perl will try to convert the operands to numbers.
It does this by ignoring leading whitespace, they taking the decimal digits up to the end of the string or the first non-decimal digit. It will also accept the first decimal point and the decimal digits that follow it. We cover this in Learning Perl, but you can also read about it in perlnumber.
So, the string 4 - 1 is numerically 4, with a warning:
$ perl -wle " print q(4 - 1) + 0 "
Argument "4 - 1" isn't numeric in addition (+) at -e line 1.
4
I think you're asking a slightly different question. Perl does not do math for you. You have an eval in there, and that will execute the string as Perl code. What the Perl code evaluates to is what you get back.
$a="";
$b="n";
$c = !$a;
$d = !$b;
print $c , "\n" , $d;
if($d == 0){
print "zero";
}
I wrote this perl program i expected the output to be as follows
1
0Zero
but it prints
1
zero
can any one explain me why is it like that??
Variables in Perl are treated as either numbers or strings depending on the context. If you haven't treated something as a number when assigning to it, Perl will treat it like a string when you print it.
Because of this, false values are a bit different in Perl than in languages with stronger typing (emphasis added by me):
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. Most Perl operators that return true or false behave
this way.
So, the issue here is that ! is a logical operator rather than an arithmetic one. Therefore, it returns a logically false value, which is represented in different ways depending on the context.
If you want to make sure something is treated as a number, you have a few options. You can perform an arithmetic operation, which will make Perl consider the result a number:
$d=!$b+0;
print $d;
You can use sprintf or printf to explicitly control the display:
printf '%d', $d;
Or you can use int:
print int $d;
(Note: this all may seem a bit complicated. But it was designed so that the language will just do what you want, and you won't have to think about it. And normally it does. There are only occasional edge cases where you need to do something other than Perl's default behavior.)
In perlsyn, under Truth and Falsehood you read
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. Most Perl operators that return true or false behave this way.
This is what you experience. $a is false value, so !$a, assigned to $c, becomes a true value, accordingly printed as 1. $b is a true value, so !$b, assigned to $d, becomes that special false value. In the print statement (print $c , "\n" , $d;), it is evaluated in string context, so the empty string is printed. The if statement ($d == 0) evaluates it in number context, so it is a 0, hence zero is printed.
!$b evaluates to empty string '', as false values are not represented by zero when stringified by print(), "$bool", or some other string operation.
$a="";
$b="n";
$c = !$a;
$d = !$b;
print $c , "\n" , $d;
if($d == 0){
print "zero";
}
use Data::Dumper;
print Dumper {
'$a' => $a,
'$b' => $b,
'$c' => $c,
'$d' => $d,
'$d == 0' => $d == 0,
};
output
$VAR1 = {
'$d == 0' => 1, # boolean true
'$b' => 'n',
'$d' => '', # boolean false
'$c' => 1,
'$a' => ''
};
Perl operators such as ! that return boolean values return a special value for false: it is "" when used in string context and 0 when used in numeric context.
So when you print it, it is "", not "0". But if you try to use it as a number, it doesn't give a warning the way "" does. Compare:
perl -wle'$false = !1; print "string: ", $false, " number: ", 0+$false'
string: number: 0
and
perl -wle'$empty = ""; print "string: ", $empty, " number: ", 0+$empty'
Argument "" isn't numeric in addition (+) at -e line 1.
string: number: 0
On the accepted answer for
String compare in Perl with "eq" vs "=="
it says that First, eq is for comparing strings; == is for comparing numbers.
"== does a numeric comparison: it converts both arguments to a number and then compares them."
"eq does a string comparison: the two arguments must match lexically (case-sensitive)"
You can ONLY use eq for comparing strings but
both eq AND == works for comparing numbers
numbers are subset of strings so i just dont understand why you would ever use ==
Is there a reason why you would want to use == for comparing numeric values over just using eq for all?
Here is an example of why you might want ==:
$a = "3.0";
print "eq" if $a eq "3"; # this will not print
print "==" if $a == 3; # this will print
3.0 is numerically equal to 3, so if you want them to be equal, use ==. If you want to do string comparisons, then "3.0" is not equal to "3", so in this case you would use eq. Finally, == is a cheaper operation than eq.
String comparisons are just plain different, especially with numbers.
#s_num=sort {$a <=> $b} (20,100,3); # uses explicit numeric comparison
print "#s_num\n"; # prints 3 20 100, like we expect
#s_char=sort (20,100,3); # uses implicit string comparison
print "#s_char\n"; # prints 100 20 3, not so good.
-Tom Williams
If a string in Perl 5 passes looks_like_number, it might as well be a number. For instance,
my $s = "10" + 5;
results in $s being assigned 15.
Are there any cases where a string does not behave like its numeric equivalent would?
When dealing with bitwise operators. 123 ^ 456 is 435, but "123" ^ "456" is "\x05\x07\x05". The bitwise operators work numerically if either operand is a number.
I can only think of one: when checking for truth. Strings that are equivalent to 0, but that are not "0", such as "0.0", "0 but true", "0e0", etc. all pass looks_like_number and evaluate to 0 in numeric context, but are still considered true values.
An equivalent number and string behave differently in hash keys -- or, more generally, any time we stringify a largish number:
my (%g, %h);
$g{ 1234000000000000 } = undef; # '1.234e+015' => undef
$h{'1234000000000000'} = undef; # '1234000000000000' => undef
Note that we are still within the range where Perl can store the number precisely:
> perl -e 'printf qq{%.f\n}, 1234000000000000 + $_ for +1, 0, -1'
1234000000000001
1234000000000000
1233999999999999
DB<1> sub is_num { my $x = shift; "$x " ~~ $x }
DB<2> print is_num(123)
1
DB<3> print is_num('123')
DB<4>
I have the following piece of code in my program:
$val = chr(someFunction());
if($val == " ")
{
#do something
}
elsif($val == 0)
{
#do something else
}
But whenever 0 is passed to $val, the if part executes instead of the elsif which I expect to get executed.
How can I fix this?
Thank You.
The == operator is used to compare numeric values. If you want to compare strings, you should use the eq operator.
if ($val eq " ") ...
There are several ways to fix this (TIMTOWDI). You could import the looks_like_a_number function from the standard Scalar::Util package:
if (looks_like_a_number($val) and $val == 0) {
#do something
}
You could use the string equality operator
if ($val eq 0) {
#do something
}
If you have Perl 5.10, you could use the smart match operator
if ($val ~~ 0) {
#do something
}
And many more. Which method you use depends heavily on what you are trying to achieve.
If you had warnings enabled, you would have known what the problem was.
Run this:
use strict;
use warnings;
my $val = chr(someFunction());
if($val == " ")
{
#do something
}
elsif($val == 0)
{
#do something else
}
sub someFunction {
return 1;
}
And you get:
C:>test.pl
Argument " " isn't numeric in numeric eq (==) at C:\test.pl line 6.
Argument "^A" isn't numeric in numeric eq (==) at C:\test.pl line 6.
Adding use diagnostics gives us this additional explanation:
(W numeric) The indicated string was fed as an argument to an operator
that expected a numeric value instead. If you're fortunate the message
will identify which operator was so unfortunate.
So, since we don't want numeric eq, we want string eq: eq. If you didn't know that already, you could look in perldoc perlop to read about Equality Operators.
This is a classic example of how using the warnings and strict pragmas saves time.