wawawawa
wawawawa

Reputation: 431

Evaluating Statements with '||' in Perl

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

Answers (7)

Joel Berger
Joel Berger

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

TLP
TLP

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

mob
mob

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

Toomai
Toomai

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

Dave Cross
Dave Cross

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

zoul
zoul

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

Toto
Toto

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

Related Questions