Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

?: and saving backreferences

by throop (Chaplain)
on Dec 05, 2006 at 23:34 UTC ( #588002=perlmeditation: print w/replies, xml ) Need Help??

Another reason to use ?: rather than if/else. ?: saves your backreferences longer. The following code loses
$_ = 'abcdefghijk'; my($left) =1; if($left){ /ab(..)ef/} else{ /gh(..)jk/}; print "Caught $1\n"
because the $1 backreference is lost once you exit the scope of the if/else. So you can clutter things up with an extra variable:
$_ = 'abcdefghijk'; my($left, $var) =1; if($left){ ($var) = /ab(..)ef/} else{ ($var) = /gh(..)jk/}; print "Caught $var\n"
or you can use ?: and not lose the backreference
$_ = 'abcdefghijk'; my($left) =1; $left ? /ab(..)ef/ : /gh(..)jk/; print "Caught $1\n"
Of course, simple examples aren't as compelling as complex ones (because the amount of extra obscurity and work are small when the example is simple.) But if the pattern match is complex (eg when I'm trying to pull out $4 instead of $1) or when I'm ganging different patternmatches together. In the example below, $verbpat, $nounpat, $preppat are three similar patterns; I want to pull $4 out of each
$verbtest ? /$verbpat/ : $nountest ? /$nounpat/ : $preptest ? /$preppat/ : die 'No catch on $_'; $catch = $4;
So here's one more time that ?: is superior to if/else - when you want the backreference for a little longer while.

Replies are listed 'Best First'.
Re: ?: and saving backreferences
by brian_d_foy (Abbot) on Dec 06, 2006 at 06:14 UTC

    You don't need clutter just to use a variable. Remember that a do block returns the last evaluated expression. In this example, I assign that result to @memories, which puts the matches in list context so they return the parts they remembered as a list.

    $_ = 'abcdefghijk'; foreach my $left ( 0 .. 1 ) { my @memories = do { if( $left ) { /ab(..)ef/ } else { /fg(..)jk/ } }; print "Left $left: caught @memories\n" }

    Once I have the memories in the array, I can easily pull out any element that I like. If the match fails, @memories is empty. I don't have to deal with a lot of gymnastics to figure out is the match succeeded. Without knowing that the match suceeded, you shouldn't use any of the number variables ($1, $2, and so on).

    Remember that $1 and so on are package variables, and that they persist until the next successful pattern match. It doesn't have anything to do with lexical scope. Actually, perlvar says:

    $<digits> Contains the subpattern from the corresponding set of capturing parentheses from the last pattern match, not counting patterns matched in nested blocks that have been exited already. (Mnemonic: like \digits.) These variables are all read-only and dynamically scoped to the current BLOCK.
    brian d foy <>
    Subscribe to The Perl Review
      Perhaps if/elsif/else could have a continue wherein one could snarf up the variable-values local to the if/elsif/else scope.


Re: ?: and saving backreferences
by ikegami (Pope) on Dec 05, 2006 at 23:50 UTC

    None of those are particularly useful in practice since they don't check if the regexp matched successfully (and don't lend well to being modified to do so). When the regexp doesn't match, $1 and similar variables are left unchanged and therefore meaningless. What follows are more practical patterns.

    Using ?::

    my $re = $left ? qr/ab(..)ef/ : qr/gh(..)jk/; /$re/ or die("Bad data"); print "Caught $1\n"

    Using if/elsif:

    my $re; if (...) { $re = qr/.../ } elsif (...) { $re = qr/.../ } elsif (...) { $re = qr/.../ } else { die '...' } /$re/ or die("Bad data"); print "Caught $1\n"

    Using a lookup table:

    my %re_lookup = ( ... => qr/.../, ... => qr/.../, ... => qr/.../, ); /$re_lookup{...}/ or die("Bad data"); print "Caught $1\n"
      Maybe I'm missing something, but it seems fairly easy to modify OP's code to check for a match:
      ($left ? /ab(..)ef/ : /gh(..)jk/) or die("Bad data");
      This line is less readable in some ways than your suggested patterns, but it doesn't introduce any new variables.
      Note: I got some results that looked funny until I replaced OP's (..) with (.*) in the regexen, watch out for that!
Re: ?: and saving backreferences
by BUU (Prior) on Dec 06, 2006 at 05:31 UTC

    I'm rather confused as to what "because the $1 backreference is lost once you exit the scope of the if/else" means to you, to me it sounds like you think $1 is cleared after you exit the scope it was declared in, which is clearly not true.

    Am I missinterpreting your statements some how?

    Yeah, I was missinterpreting. Consider this retracted.

      Did you run the code the OP posted? Since at least 5.6.1, the $1 set by the regexp inside the if is no longer in scope outside the if. The following illustrates this clearly.

      'a' =~ /(.)/; { 'b' =~ /(.)/; print("$1\n"); # b } print("$1\n"); # a

      This is documented in perlvar.

        You are quite correct and I was missinterpreting his comment. Consider it retracted.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://588002]
Approved by Joost
Front-paged by grinder
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (2)
As of 2021-05-15 09:26 GMT
Find Nodes?
    Voting Booth?
    Perl 7 will be out ...

    Results (150 votes). Check out past polls.