http://qs321.pair.com?node_id=11118782

RedJeep has asked for the wisdom of the Perl Monks concerning the following question:

Ok, this should be very basic. For the life of me I can't figure it out. I searched and searched. Maybe you can help?

In one of my programs I found this wrong way

if ($sender_transaction_type !~ /3|21/)

The above code is wrong, is bad, works sometimes. I know this is wrong. Trying to figure out the right way. I wrote the little test script below.

In the below script the multi-compare Option Equals works fine. But the multi-compare Option Not Equal does not work. Why? How to fix?

For example, set $a = 77 and the Option Not Equal does not work.

Thanks for the help!

use strict; $a = 8; #Try using 7 and see how second loop does not evaluate correct +ly ## Examples # $a = 44 evaluates ok for both options # $a = 33 evaluates ok for both options # $a = 3 does not evalute correctly on Option Not Equal # $a = 77 does not evalute correctly on Option Not Equal # $a = 8 does not evalute correctly on Option Not Equal ## Option Equal if (($a == 3) || ($a == 77) || ($a == 8)) { print "$a must be a 3 77 or 8\n"; } else { print "$a is not a 3 77 or 8\n"; } print "\n"; ## Option Not Equal if (($a != 3) || ($a != 77) || ($a != 8)) { print "$a is not a 3 77 or 8\n"; } else { print "$a is a 3 77 or 8\n"; }

Replies are listed 'Best First'.
Re: Multiple numeric not or compare in if statement
by syphilis (Archbishop) on Jul 02, 2020 at 05:08 UTC
    if (($a != 3) || ($a != 77) || ($a != 8))

    Try instead:
    if (($a != 3) && ($a != 77) && ($a != 8))

    Cheers,
    Rob
      OP: maybe it was an oversight, but when changing the nested logic in a condition such as that you must necessarily change the higher level logic if you wish for the original to be equivalent exactly opposite to the negated version.

      if (($a == 3) || ($a == 77) || ($a == 8)) -- is exactly opposite to -- if (($a != 3) && ($a != 77) && ($a != 8))

      Update: correct - meant to show exact opposites, used the most wrong possible to describe this.

        if (($a == 3) || ($a == 77) || ($a == 8)) -- is equivalent to -- if (($a != 3) && ($a != 77) && ($a != 8))

        No, those two aren't equivalent, they're exact opposites. It seems you didn't apply what you said:

        when changing the nested logic in a condition such as that you must necessarily change the higher level logic if you wish for the original to be equivalent to the negated version.
        if (($a == 3) || ($a == 77) || ($a == 8)) -- is equivalent to -- if (!( ($a != 3) && ($a != 77) && ($a != 8) ))
Re: Multiple numeric not or compare in if statement
by kcott (Archbishop) on Jul 02, 2020 at 05:42 UTC

    G'day RedJeep,

    There's short-circuiting occurring which I think may be throwing you. (77 is not equal to 3) is TRUE; so, when $a is 77, ($a != 3) is TRUE. Having found a TRUE condition, there's no need to evaluate the other ORed conditions (so Perl doesn't).

    Changing your Option Not Equal condition to completely negate the Option Equal condition will do what I think you want:

    $ perl -E '$a = 77; if (($a == 3) || ($a == 77) || ($a == 8)) { say "i +s" } else { say "not" }' is $ perl -E '$a = 77; if (!(($a == 3) || ($a == 77) || ($a == 8))) { say + "not" } else { say "is" }' is

    You can run that rather complicated condition through B::Deparse to find a simpler way of expressing it.

    $ perl -MO=Deparse -e '(!(($a != 3) || ($a != 77) || ($a != 8)))' not $a != 3 || $a != 77 || $a != 8; -e syntax OK $ perl -E '$a = 77; if (not $a != 3 || $a != 77 || $a != 8) { say "not +" } else { say "is" }' is

    If it helps you, perlop has an "Operator Precedence and Associativity" section.

    — Ken

Re: Multiple numeric not or compare in if statement
by LanX (Saint) on Jul 02, 2020 at 05:36 UTC
Re: Multiple numeric not or compare in if statement
by AnomalousMonk (Archbishop) on Jul 02, 2020 at 06:22 UTC
Re: Multiple numeric not or compare in if statement
by hippo (Bishop) on Jul 02, 2020 at 09:02 UTC

    You have good answers already so I'll merely point out 2 things which might make things a little easier for you in general.

    Did you know that the || operator has lower precedence than either == or !=? This means that all your inner brackets can go and you can write your conditionals like this:

    if ($a == 3 || $a == 77 || $a == 8)

    Further, since not has lower precedence than && you could even write the negated one as

    if (not $a != 3 && $a != 77 && $a != 8)

    Finally, $a and $b are special variables (see perlvar) which means that you would be best to avoid them even in an SSCCE. I'd recommend starting from the other end of the alphabet if you want to use single-character names.

Re: Multiple numeric not or compare in if statement
by davido (Cardinal) on Jul 02, 2020 at 16:53 UTC

    Also worth mentioning: If your list of comparisons starts growing, grep is useful.

    if (grep {$a == $_} (3,77,8)) {...} # If any of them match. if (!grep {$a == $_} (3,77,8)) {...} # If none of them match. if (0 == grep {$a == $_} (3,77,8)) {...} # Same as above but more expl +icit. if (3 == grep {$a != $_} (3,77,8)) {...} # Also if none of them match, + but maybe harder to follow.

    For a list of three things, it may be more cognitive load to understand. But when the list grows to five or ten things, the grep becomes more palatable.


    Dave

Re: Multiple numeric not or compare in if statement
by jo37 (Deacon) on Jul 02, 2020 at 13:41 UTC
    The above code is wrong, is bad, works sometimes. I know this is wrong.

    You didn't say in which way it should be wrong. It is not wrong, though it might do something different from your expectations.

    $sender_transaction_type !~ /3|21/
    This is true, if $sender_transaction_type does not contain the digit 3 or the digit sequence 21. For e.g. "35" or "421" it is false. Maybe you expected it to check for equal / not equal, which it doesn't. But it's not wrong.

    To check if a string is not in a given set, you may use this expression:

    $sender_transaction_type !~ /^(?:3|21)$/
    But keep in mind that these are string operations where "3", "3e0" and "3." are all different, contrary to their numerical equality.

    Greetings,
    -jo

    $gryYup$d0ylprbpriprrYpkJl2xyl~rzg??P~5lp2hyl0p$
      Also note that "2\n" matches the regex. If you don't want that, use \z instead of $.
      map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]