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

Why do these two snippets print different things? ($1 vs $_, for the impatient):

perl -e '$a="1b2cde5";for($a=~/(\d+)/g){$t+=$1;};print "$t\n";' perl -e '$a="1b2cde5";for($a=~/(\d+)/g){$t+=$_;};print "$t\n";'

The first one prints 15. The second one prints 8.

My confusion is with the first one. I can understand it's taking the rightmost match, but in that case why does the loop iterate three times? I would have thought if the loop iterated three times, it would put different values into $1 each time. I'm trying to understand this one. Any explanations appreciated. This is perl 5.8.4.

Replies are listed 'Best First'.
Re: Looping on a match, and value of $1
by diotalevi (Canon) on Jul 24, 2004 at 00:54 UTC

    The first loop executed $a =~ /(\d+)/g once producing a list of "1", "2", and "5". The last iteration of the expression left $1 set to "5" so when $_ was "1", you added "5". When $_ was "2" you added "5". When $_ was "5", you added "5".

    Try the first one replacing for() with while() and you'll see what you expected. Look in perlre at the 'g' flag in list versus scalar context. Also look at perlsyn for the difference between foreach and while.

      Think it is just a typo or the expression was not precise, this left me wondering: The last iteration of the expression left $1 set to "5"

      His original code has two seperate perl -e, so this does not really fit, as the two for loops are not related at all. Even if there is only one perl -e, $1 still will be reset when the second // is executed.

      But I believe diotalevi made the point (precisely in his mind). The whole point is that, the // is not executed for each iteration, but once at the begin. The for loop does not loop through the //, but rather the array produced by //. $_ always takes the current value of the current element when it loops, but $1 has nothing to do with the loop, and its value stays.

      Thought just add a bit code to back up what he said (there will never be any harm to do a little bit experiment):

      use strict; use warnings; my $t; #comment out one of the next two lines #$a="1b2cde5";for($a=~/(\d+)/g){display($_, $1); $t+=$1;};print "$t\n" +; $a="1b2cde5";for($a=~/(\d+)/g){display($_, $1); $t+=$_;};print "$t\n"; sub display { print "_ = " . shift() . ", 1 = " . shift() . "\n"; }

      Comment out the first one, you get:

      _ = 1, 1 = 5 _ = 2, 1 = 5 _ = 5, 1 = 5 8

      Comment out the second one, you get:

      _ = 1, 1 = 5 _ = 2, 1 = 5 _ = 5, 1 = 5 15
Re: Looping on a match, and value of $1
by NetWallah (Canon) on Jul 24, 2004 at 01:06 UTC
    I think the cause of the confusion is :
    You are expecting the RegEx to be evaluated multiple times.

    With the "g" modifier in a list context (which the "for" provides), the regex is evaluated only ONCE, returning a list of matches, which get successively aliased to "$_".

    Since it is evaluated only once, the last match (value 5) is stored in $1.

    Update Oops - Beaten to the punch in explaining.

    However, try the Regex in a SCALAR context, and it will behave as you expect (diotalevi's suggestion):

    # Note: Changed "for" to "while" for scalar context: $a='1b2cde5';while($a=~/(\d+)/g){$t+=$1;}; print qq($t\n) --Output-- 8

        Earth first! (We'll rob the other planets later)

Re: Looping on a match, and value of $1
by Fletch (Bishop) on Jul 24, 2004 at 00:58 UTC

    Bah, beaten to the explanation. At any rate, you can see this if you go into the debugger (perl -de 0) and examine the list produced and $1.

    DB<1> $a = "1b2cde5" DB<2> x $a =~ /(\d+)/g, $1 0 1 1 2 2 5 3 5