Reputation: 431
Can someone please explain what's going on here, please?
I've simplified as much as I can.
( 1 || 2 == 1 ) is TRUE (as expected)
( 1 || 2 == 2 ) is TRUE (as expected)
I would also expect the following to both be true (but this may suggest a lack of understanding...)
( 1 == ( 1 || 2 ) ) is TRUE
( 2 == ( 1 || 2 ) ) is FALSE <--- !!! I don't understand this..
Now it starts getting a bit odd...
( 2 == 1 || 2 ) is TRUE
( 3 == 1 || 2 ) is TRUE <--- !!! I don't understand this..
After some more playing about, I find the following:
( 1 == ( 1 || 2 ) ) is FALSE
( 2 == ( 1 || 2 ) ) is TRUE
( 1 == ( 1 && 2 ) ) is TRUE
( 2 == ( 1 && 2 ) ) is FALSE
You might have guess that I'm trying to do something along the lines of:
sub foo {
# ... blah blah ...
return 0 if $status == ( 2 || 4 || 7 || 9);
# ... do other stuff ...
return 1;
}
my $result = foo();
if ($result) {
# then do something based on a result
} else {
# log failure or whatever
}
I know I can use this idiom, but the extra "$status"(es) seem superfluous:
return 0 if ( $status == 1 || $status == 4 || $status == 7 ); # and so on...
I also know I could use a regex with alternation or to check if the value is in an array of the allowable values.
But I'd like to understand why this is not as I expect.
Upvotes: 3
Views: 153
Reputation: 20280
You see now that its the binary-ness of the operators that give them their behavior. Much stranger is the world of Quantum Mechanics, from which Quantum::Superpositions
draws inspiration.
Try this for some fun (it does something a lot closer to your original expectations):
#!/usr/bin/env perl
use strict;
use warnings;
no warnings 'qw';
use Quantum::Superpositions;
my @cases = qw'
(any(1,2)==1)
(any(1,2)==2)
(1==any(1,2))
(2==any(1,2))
(2==any(1,2))
(3==any(1,2))
(1==any(1,2))
(2==any(1,2))
(1==all(1,2))
(2==all(1,2))
';
print "$_ -> ", eval, "\n" for @cases;
of course you can do even stranger things with these superposition states, like products of states etc. The whole thing is good for some light reading (if you're as geeky as I am).
Reading some of the other answers, this is also like the Perl6::Junctions
module mentioned by zoul
Upvotes: 1
Reputation: 67940
You have been given explanations on the OR logic, so I will expend with that, just tell you that a precedence table can be found in perldoc perlop. You should be able to figure out the logic with that information.
What you are looking for, though, is probably the functionality of a switch statement:
use v5.10;
sub foo {
my $status = shift;
given ($status) {
when ([2,4,7,9]) { return 0 }
default { return 1 }
}
}
Upvotes: 4
Reputation: 118695
$status == ( 2 || 4 || 7 || 9)
will not do what you expect. (And if it did do what you expect, what do you think $status == (2 && 4 && 7 && 9)
should mean?) That statement is equivalent to
$status == 2
||
and &&
are binary operators that always return one of their scalar operands. It may help to think of them as equivalent to these binary functions:
sub OR {
my ($a,$b) = @_;
if ($a) {
return $a;
} else {
return $b;
}
}
sub AND {
my ($a,$b) = @_;
if ($a) {
return $b;
} else {
return $a;
}
}
(I'm glossing over the short-circuiting feature of ||
and &&
, but that's not so important for this post).
Noting that ==
has higher precedence than ||
and &&
, we can rewrite all your expressions in terms of AND
and OR
:
( 1 || 2 == 1 ) --> (OR(1,2==1)) ---> OR(1,0) --> 1 TRUE
( 1 || 2 == 2 ) --> (OR(1,2==2)) ---> OR(1,1) --> 1 TRUE
These two expressions are both true, but NOT for the reason you were expecting.
( 1 == ( 1 || 2 ) ) --> (1 == OR(1,2)) ---> (1==1) TRUE
( 2 == ( 1 || 2 ) ) --> (2 == OR(1,2)) ---> (2==1) FALSE
( 2 == 1 || 2 ) --> OR(2==1,2) ---> OR(0,2) --> 2 TRUE
( 3 == 1 || 2 ) --> OR(3==1,2) ---> OR(0,3) --> 3 TRUE
( 1 == ( 1 || 2 ) ) --> (1==OR(1,2)) ---> (1==1) TRUE, not FALSE
( 2 == ( 1 || 2 ) ) --> (2==OR(1,2)) ---> (2==1) FALSE, not TRUE
( 1 == ( 1 && 2 ) ) --> (1==AND(1,2)) ---> (1==1) TRUE
( 2 == ( 1 && 2 ) ) --> (2==AND(1,2)) ---> (2==1) FALSE
And your original idea of checking
$status == ( 2 || 4 || 7 || 9)
is equivalent to
$status == OR(OR(OR(2,4),7),9)
$status == OR(OR(2,7),9)
$status == OR(2,9)
$status == 2
Upvotes: 10
Reputation: 4244
The ||
statement cannot be used in the way you'r trying to use it. It makes sense in English to say "if 1 is either 1 or 2", but in most if not all programming languages you must say "if 1 is 1 or 1 is 2" (that is, 1 == 1 || 1 == 2
or some macro thereof).
What's actually happening is that ||
and &&
, when used on two numbers, are evaluating to the first nonzero value they find. So 1 || 6 -> 1
while 6 || 1 -> 6
. Combined with ==
being evaluated first if no parentheses are involved, this results in:
( 1 || 2 == 1 ) -> (1) == 1 -> true
( 1 || 2 == 2 ) -> (1) == 2 -> false
( 1 == ( 1 || 2 ) ) -> 1 == (1) -> true
( 2 == ( 1 || 2 ) ) -> 2 == (1) -> false
( 2 == 1 || 2 ) -> false || 2 -> 2 -> true
( 3 == 1 || 2 ) -> false || 2 -> 2 -> true
( 1 == ( 1 || 2 ) ) -> 1 == (1) -> true
( 2 == ( 1 || 2 ) ) -> 2 == (1) -> false
( 1 == ( 1 && 2 ) ) -> 1 == (1) -> true
( 2 == ( 1 && 2 ) ) -> 2 == (1) -> false
Upvotes: 5
Reputation: 69314
The ||
operator simply doesn't work as you think (hope?) it does. You can see this by printing out the results of some expressions.
$ perl -E'say 1 || 2' # 1
$ perl -E'say 2 || 1' # 2
The operator is lazy. It only evaluates operands until it knows what the result is going to be.
Two ways to achieve what you want.
1/ Create a hash that contains the values you're interested in.
my %good_status = map { $_ => 1 } qw[2 4 7 9];
return if $good_status{$status};
2/ Use the any
function from List::MoreUtils.
use List::Utils 'any';
return if any { $status == $_ } qw[2 4 7 9];
Upvotes: 5
Reputation: 104145
( 2 == ( 1 || 2 ) ) is FALSE <--- !!! I don't understand this..
The ||
operator returns first “true” value it encounters or false. Therefore, 1||2
evaluates as 1
and the numeric comparison to 2
fails. As for your code, you might use Perl6::Junction:
if ($status == any(1, 4, 7)) {
...
}
Upvotes: 5
Reputation: 91518
( 2 == ( 1 || 2 ) ) is FALSE <--- !!! I don't understand this..
( 1 || 2 )
is evaluated first at 1
then compared with 2
==> of course false!!
( 3 == 1 || 2 ) is TRUE <--- !!! I don't understand this..
3 == 1
is evaluated first and result is 0/false
then || 2
returns 2
<==> True.
return 0 if ( $status == 1 || $status == 4 || $status == 7 );
can be rewritten like:
return 0 if $status =~ /^[147]$/;
Upvotes: 3