Reputation: 30885
I'm parsing xml using XML::Parser
; I have the tags "my_id" and "total" in my xml.
When I try to combine both with "or", it always evaluating the if as true:
if($elt eq "my_id" or "total")
{
return 1;
$context = $elt;
}
If I break it to:
if($name eq "my_id")
{
print($name." ".$xml_tag_val."\n");
return 1;
}
elsif($name eq "total")
{
print($name." ".$xml_tag_val."\n");
return 1;
}
It is working just fine. What is wrong with the first if?
Upvotes: 0
Views: 89
Reputation: 67900
The main problem here is that or
does not work that way. This is a common misconception about how or
works. You can use an expression in eq
, but the expression you have uses or
, which has lower precedence than eq
, and therefore is not part of the string comparison. Also, an or-expression only ever returns one of its parameters, whichever is true. And with strings, everything except 0
or the empty string is true. This is the reason your statement always returns true: "total"
is a true expression in Perl.
What you have written can be clarified with parentheses:
if( ( $elt eq "my_id") or "total")
# ^---- -----^
Where you can clearly see that or "total"
will make the expression always true. Note also that it would be wrong to do this:
if( $elt eq ( "my_id" or "total") )
Because the or
:ed expression would always return "my_id"
and never even check "total"
.
What you need to do is to do the check once for each parameter:
if( ($elt eq "my_id") or ($elt eq "total") )
Or if you have a list of parameters, and $elt
contains a non-false value (i.e. 0
or empty string, or undef
), you can use grep
:
if (grep { $elt eq $_ } @parameters)
Or more correctly, use the any
function from List::MoreUtils
:
use List::MoreUtils qw(any);
if ( any { $elt eq $_ } @parameters )
Upvotes: 4
Reputation: 33116
$elt eq "my_id" or "total"
As others have noted, this is not doing what you intend. With parentheses, this is equivalent to ($elt eq "my_id") or "total"
. The expression is always true because even if $elt
is not string-equal to "my_id"
, the second part of the expression, "total"
, is a truthy expression.
The perl motto is "there is more than one way to do it." This certainly applies here; there are a number of ways to test whether $elt
is string-equal to either 'my_id'
or 'total'
.
if (($elt eq 'my_id') || ($elt eq 'total')) ...
if ($elt eq 'my_id' || $elt eq 'total') ...
I prefer the first of the above. My personal operator preference rule: Use parentheses except for expressions such as $a+$b*$c
. You don't need parentheses for $a+$b*$c
because everyone knows that multiplication and division distribute over addition and subtraction. For anything else, use parentheses. But that's just my preference. There's more than one way to do it.
if ($elt =~ /^(?:my_id|total)$/) ...
That's a completely different way to do it. The parentheses are important here.
For yet another way to do it, use a post-if:
return print("$name $xml_tag_val\n") if $elt =~ /^(?:my_id|total)$/;
You don't need that $name." ".$xml_tag_val."\n"
stuff. Learn the difference between '$non_interpolated_string'
and "$interpolated_string"
. Single quotes and double quotes are rather different beasts in perl.
Upvotes: 0
Reputation: 53478
That eq
doesn't apply to the left hand side of the "or". The precedence of the eq
is higher, so it tests that first.
That first expression is actually:
if ( $elt eq "my id"
or "total" );
Because "total" is a string, it's therefore true. You're not testing it's equivalence at all.
You could rewrite as:
if ( $elt eq "my id"
or $elt eq "total" ) {
#....
}
Upvotes: 0
Reputation: 91385
You have to write your conditions like this:
if($elt eq "my_id" or $elt eq "total")
Upvotes: 3