Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

Mutable foreach list?

by zigdon (Deacon)
on Jan 11, 2007 at 15:19 UTC ( #594174=perlquestion: print w/replies, xml ) Need Help??

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

I was talking to a friend about foreach aliasing the loop variable one by one through the list provided... As he was playing around with it, came across this:

use Data::Dumper; @headers = ("header 1","header 2","header 3"); @body = ("body A","body B","body C"); $h = 4; foreach (@headers,"",@body) { print "$h: $_\n"; @body = (); push @headers,"header ".$h++; } print "\n", scalar @headers, "\n", Dumper \@headers;
Which produces:
4: header 1 5: header 2 6: header 3 7: 8: header 4 9: header 5 10: header 6 10 $VAR1 = [ 'header 1', 'header 2', 'header 3', 'header 4', 'header 5', 'header 6', 'header 7', 'header 8', 'header 9', 'header 10' ];

And I say huh? I follow until output line "7:". I can even see why it would run 3 more times (since the list fed to foreach was evaluated before @body was modified).

What I don't get is how the next 3 elements of @headers get in there? Is this part of the "don't mess with the loop vars while looping" clause?

What am I missing?

-- zigdon

Replies are listed 'Best First'.
Re: Mutable foreach list?
by Fletch (Chancellor) on Jan 11, 2007 at 15:27 UTC

    It says on the label "don't muck with arrays you're looping over", you muck with the array, and you get strange behavior. What's to not understand?

    While I concede it might be interesting to try and figure out what execution path is happening to cause the results, you're in doctor-it-hurts-when-I-do-this territory and shouldn't count on any particular behavior being repeatable or stable.

      Right - I'm on forbidden ground. Perlsyn says:
      If any part of LIST is an array, "foreach" will get very confused if you add or remove elements within the loop body, for example with "splice". So donít do that
      I guess what's happening is something like this (psudocode):
      $it = &make_list_into_iterator(LIST); $count = $it->figure_out_element_count; while ($count-- > 0) { $_ = $it->next; CODE }
      Which fits the behaviour I see.

      -- zigdon

Re: Mutable foreach list?
by almut (Canon) on Jan 11, 2007 at 15:35 UTC

    "aliasing the loop variable" means that the $_ (in your case) will be aliased to (i.e. not be a copy of) the individual elements of the list. So, modifying $_ will modify the contents of the array. This has nothing to do with pushing stuff onto the array @headers, though...

      Oh, I totally agree. I'm just trying to understand the foreach portion - does the list given get evaluated at the first encounter? On each iteration? I guess this might really be a perlguts kind of question.

      -- zigdon

        The following are all implemented differently:

        • for (EXPR; EXPR; EXPR) ("C-style for loop", an augmented while loop.)
        • for (EXPRX..EXPRY) (A range and nothing else.)
        • for (reverse CONSTX..CONSTY) (A constant range, preceded by reverse.)
        • for (reverse EXPRX..EXPRY) (A variable range, preceded by reverse.)
        • for (@ARRAY) (An array and nothing else.)
        • for (reverse @ARRAY) (Reverse of an array and nothing else.)
        • for (reverse LIST) (Reverse of any list that doesn't fit the above patterns.)
        • for (LIST) (Any list that doesn't fit the above patterns.)

        You might find the difference between foreach (@ARRAY) and foreach (LIST) interesting.

        { my $x = 1; my $y = 4; # The initial values are saved. my @a; foreach ($x..$y) { push @a, $_; $y++; } print("@a\n"); # 1 2 3 4 } { my $x = 1; my $y = 4; # The initial values are saved. my @a; foreach (reverse $x..$y) { push @a, $_; $x--; } print("@a\n"); # 4 3 2 1 } { my @a = (1, 2, 3, 4); my $i = 5; # Loops "while (pass_num < @a)". # In this case, that means loop forever. foreach (@a) { # Loops "while (pass_num < @a)" push(@a, $i++); if (@a == 20) { push(@a, '...'); last; } # Avoid infinite loop. } print("@a\n"); # 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ... } { my @a = (1, 2); my @b = (3, 4); my $i = 5; # Creates a list at the start of the # loop and iterates over that list. # In this case, elements are added to @b, # but not to the list on the stack, so # it loops 4 times. foreach (@a, @b) { push(@b, $i++); } print("@a @b\n"); # 1 2 3 4 5 6 7 8 }

        The difference between
        foreach (reverse CONSTX..CONSTY)
        and
        foreach (reverse EXPRX..EXPRY)
        is that the list in built at compile time in the former.

        >perl -le "for (1..2) { for (reverse 1..3) { print; $_=5; } }" 3 2 1 5 5 5 >perl -le "for (1..2) { for (reverse 1..($x=3)) { print; $_=5; } }" 3 2 1 3 2 1

        Empirical evidence suggests ;) the list given to foreach (...) is not being re-evaluated upon every iteration -- though I wouldn't want to rely on it... As Fletch already said, just "don't muck with arrays you're looping over".

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://594174]
Approved by andyford
Front-paged by andyford
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (5)
As of 2019-06-17 22:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Is there a future for codeless software?



    Results (80 votes). Check out past polls.

    Notices?
    • (Sep 10, 2018 at 22:53 UTC) Welcome new users!