http://www.perlmonks.org?node_id=992160

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

Quick question:

Is there a shorter way to write this line:

($var eq 'foo' || $var eq 'bar')

without having to enter $var twice other than using a regexp?

($var =~ /^(?:foo|bar)$/)

Thanks!

Replies are listed 'Best First'.
Re: var comparison
by MidLifeXis (Monsignor) on Sep 06, 2012 at 18:48 UTC

    ( grep { $var eq $_ } qw( foo bar ) ), but I would probably stick to your first example. This is close to the point where I would refactor out the comparison into a sub named with a description of what I was trying to check.

    --MidLifeXis

      Thanks MidLifeXis. I like the grep way.

      Just... Why would you prefer the first option over using a regexp? This simplified example shows only two elements to compare but if there were more the expression could get quite long.

        Clarity of intent.

        • The first indicates (to me) that you are doing a whole string comparison. There is only one thing that the eq operation is able to do.
        • The regexp opens the possibility that there is something more complex happening. Even in this simple case, I still would need to switch languages to verify that there was nothing else going on in the statement.

        Admittedly, in this case, it is not a very complex regexp, so it probably does not make much of a difference. If I am doing a full string comparison, I reach for eq. If I am matching a pattern, I reach for a regexp. It seems to me that any cue that you can give to the future-you reading your code is a good thing.

        Update: As far as what to do once it gets a number of comparisons, refactor the comparisons out into a subroutine with a descriptive name and call it.

        The following statement takes a bit to digest:

        ( $var eq 'a' || $var eq 'b' || $var eq 'c' || $var eq 'd' )

        where this replacement, at least to me, is much clearer:

        ( isAnAllowedCharacter( $var ) )

        This also allows you to change the definition of what a valid character (or whatever you are testing for) is without changing the code that is performing the test:

        sub isAnAllowedCharacter { my $testee = shift; ( $testee eq 'a' || ... ) # or perhaps %valid_characters = map { $_ => 1 } ( 'a' .. 'd' ); $valid_characters{ $testee }; # or even perhaps my $validation_service = Remote::Validation::Service->new(...); $validation_service->isValid( $testee ); }

        Update 2: Missed this one before:

        This simplified example shows only two elements to compare but if there were more the expression could get quite long.

        It seems to me that a regexp with many strings can be just as unreadable as a series of $var eq '...' comparisons. Whitespace (and /x on the regexp) can make a world of difference.

        --MidLifeXis

Re: var comparison
by Kenosis (Priest) on Sep 06, 2012 at 19:46 UTC

    Using smart matching (5.10+) is another option (unless 'clarity of intent' is compromised):

    $var ~~ [qw/foo bar/];
      carefull! better always stringify if you're not sure that its not a ref "$var" ~~ [qw/foo bar/]

        Why, with the following expected behavior?

        use Modern::Perl; my $var1 = 'foo'; my $var2 = \$var1; say $var2; # prints SCALAR(0x2590b8) say "$var2"; # prints SCALAR(0x2590b8) say $var1 ~~ [qw/foo bar/]; # is true: prints 1 say $var2 ~~ [qw/foo bar/]; # is false: prints nothing say "$var2" ~~ [qw/foo bar/]; # is false: prints nothing
      Thanks Kenosis. Smart matching promises to be a new friend from now on.
Re: var comparison
by sundialsvc4 (Abbot) on Sep 06, 2012 at 19:43 UTC

    /me nods ...

    I learned a huge lesson about computer programming when I first encountered the following comment in source-code:

    Dig me up and I’ll fix it then.

    (Blink!)

    But, you know, it’s true.   Even though none of us likes to think about mortality, our source code will probably out-live us, even by a considerable interval.   And, even if it does not, we always need to write for maintainability.   If digital computers are not quite fast enough to execute our object-code now, you can be sure they will be in the near future.   But the source-code will still be there, regardless.   Therefore, above all other things, make your intentions of the moment abundantly clear, and strive to do so in a way that will create the least amount of headaches for your successor. (Too bad for you ... sux that a bread-truck was at just the wrong place at just the wrong time ... and by-the-way so were you).   Change will come, and your if-statement will not last forever.

    Most of all, the person who makes that change wants to be able to make his or her change without disrupting, and therefore having to re-test, your original source-code.   Don’t get hung-up about “efficiency.”   Don’t indulge in cleverness of-the-moment in sacrifice of maintainability of-the-future.   If you do that, people just might throw AOL floppy-disks at your tombstone out of spite.

      I'll remember that one!

      Wow! I had never guessed how an apparently simple question would lead us to such a kind of deep thoughts.

      You are right, of course. I have to confess myself too worried about efficiency at the expense of clarity very often.

      Tip of the day: "clarity over efficiency". Or, at least, over small improvements in efficiency.

        Thanks.   (It makes me feel a wee bit younger.)

        Moore’s Law pretty much wiped-out any consideration of “efficiency” in my book, a couple of decades ago ... with the notable exception of the very valid edge-cases that my esteemed colleague, BrowserUK, routinely and legitimately encounters in his daily work.   Aside from such performance über alles edge-cases (and therefore, IMHO “as a general rule”), maintainability and simplicity trump just about every other imaginable concern in this business.   It is frankly, literally, true that ... “at 1 billion ops per second (give or take...) these days, no one can hear you scream.”

Re: var comparison
by Marshall (Canon) on Sep 07, 2012 at 12:08 UTC
    Your question is "simple" but also complex.

    My question to your question is: why?
    Shorter code doesn't mean more efficient code.
    Your first version is very clear and it will run quickly.
    String compare is a very performant operation.
    I guess that there is something about this question that you haven't told us about?

    The first version will normally execute faster and it is more clear. What is the problem?

      There were no hidden intentions. My question was simply the product of my poor knowledge of Perl :^)

      Although it's true that shorter doesn't always mean more efficient, it's also true that when you start with a language you tend to use long and complicated expressions to accomplish tasks that, once you have a little (or no so little) more knowledge, you realize that could have been done in a much shorter (and usually clearer) way.

      I was just wondering if this was the case, so the expression:

      $var eq 'foo' || $var eq 'bar'

      could be translated to something like:

      $var eq (foo|bar) (which, of course, does not work)

      or similar, the same way the defined-or operator helps to turn $x = $x // 5 into $x //= 5, for instance.

      (I understand that the implications of doing $var eq (foo|bar) would probably be more complex).

      Consider also that the case I proposed had only two options to compare, so the first version is OK, but with more options a shorter manner would be appreciated. Since that seems not possible, a function would be the best solution as suggested by MidLifeXis.

        I think that $var eq 'foo' || $var eq 'bar' will work very well. If $var is say 'xyz', the string compare will be abandoned at the first letter. "Oh, its not an 'f', lets see if the first letter is an 'b' ... The regex engine is amazing but an expression like this will just call a low level 'C' function that is very efficient.

        My point is that "shorter" source code is not necessary "better" in terms of efficiency or readability.

        If your list of possible values gets long, you could store them as keys of a hash and then check for existence or definedness in the hash.

Re: var comparison
by bduggan (Pilgrim) on Sep 10, 2012 at 01:37 UTC