Sandra Schlichting
Sandra Schlichting

Reputation: 25996

What is wrong with this if-statement?

I have this code

#!/usr/bin/perl

use warnings;
use strict;

my $nis = "qqq";
my $grp = "pre-qqq";

if ($nis eq $grp || 'pre-' . $nis eq $grp) {
    print "match1\n";
}

if (($nis || 'pre-' . $nis) eq $grp) {
    print "match2\n";
}

where the first if-statement works, but the second doesn't.

What is wrong with the second?

Can it be done without repeating the variables twice?

Upvotes: 2

Views: 237

Answers (5)

php programmer
php programmer

Reputation: 11

strpos returns the index position of where it first found the $needle, which could be 0. Since 0 also resolves to false the solution is to use strict comparison: if( false !== strpos( $haystack, $needle )...

Upvotes: 1

Dave Sherohman
Dave Sherohman

Reputation: 46187

Because the first makes (potentially) two comparisons, first comparing $nis to $grp and, if that fails, comparing 'pre-'.$nis to $grp, while the second will only ever make a single comparison, of $nis || 'pre-'.$nis against $grp.

Your problem is that $nis || 'pre-'.$nis is not a quantum superposition which can take either value depending on how you look at it, it is a single value of either $nis or 'pre-'.$nis. If $nis is truthy (which is to say, it is not an empty string, undef, the number 0, or the string "0"), then that value will be $nis. If not, that value will be 'pre-'.$nis (which, given the truthiness rules, means that 'pre-'.$nis will only ever be either 'pre-' or 'pre-0').

If you want to compare against two values, you generally need to make two comparisons... but you can do this particular comparison in one step by making the value you test against a regular expression:

if ($grp =~ /^(:?pre-)?$nis/) {
    print "match3\n";
}

I'd stick with the with your first comparison, though, for the sake of readability.

Upvotes: 5

ysth
ysth

Reputation: 98398

if ( $grp ~~ [ $nis, "pre-$nis" ] ) {
    print "match\n";
}

or (pre-5.10.1)

if ( grep $_ eq $grp, $nis, "pre-$nis" ) {
    print "match\n";
}

Upvotes: 2

Gareth McCaughan
Gareth McCaughan

Reputation: 19981

What's wrong is that "(A or B) equals C" and "(A equals C) or (B equals C)" mean completely different things. In your second if, you've asked Perl first to compute $nis || 'pre-'.$nis and then to see whether it equals $grp.

Computing $nis || 'pre-'.$nis (kinda) treats $nis and 'pre-'.$nis as boolean (true/false) values. More precisely, the result equals $nis if that's not a value Perl considers "false"; otherwise it equals 'pre-'.$nis. In this case, "qqq" is not false, so the value is just "qqq". Since this doesn't equal $grp, the condition in your second if is false.

In ordinary language, if you say "Either 3 or 6 is prime" or "Either Joe or Bob is Ellen's brother", a human listener will interpret that in the way I think you want your second if to be interpreted. But that's not how any computer language I know of works. (Semi-exception: Grisworld's "Icon".)

Upvotes: 4

esskar
esskar

Reputation: 10940

($nis || 'pre-' . $nis) evaluates to 'pre-' . $nis only if $nis is either not defined, 0, or an empty string. so in your case, it's always evaluated as if ($nis eq $grp) { ... stick with your first approach, and you will be fine

Upvotes: 1

Related Questions