in reply to Unexpected behavior of '..' lists

Closures are not relevant here:

#for (1..2) { # foreach my $x (0) { print $x++; } # Error! #} for (1..2) { foreach my $x (0..1) { print $x++; } # 01, 01 } for (1..2) { foreach my $x (0..1,5..6) { print $x++; } # 0156, 1267 }

I'm not sure why this only happens if you combine several ranges.

Actually, it also applies with just a single range:

for (1..2) { print map { $_++ } 0..1; # 01, 12 } for (1..2) { print map { $_++ } 0..1,5..6; # 0156, 1267 }

Keep in mind that foreach (expr1..expr2) is optimized into a counting loop, so $x is not an alias to a constant list in the case of foreach my $x (0..1).


You've apparently found an optimization. If you turn 0..1,5..6 into a non-constant expression, the expression rebuilds the list every time:

for (1..2) { foreach my $x (map $_, 0..1,5..6) { print $x++; } # 0156, 0156 }

Another fix is:

for (1..2) { foreach (0..1,5..6) { my $x = $_; print $x++; } # 0156, 0156 }

Personally I'd consider the fact that lists generated from ranges aren't read-only a bug.

Aye. Lists deemed constant for optimization purposes should have its values flagged read-only.

Update: Added a bit to make my explanations easier to follow. Adjusted my code to match the changes the OP did in his "Update 2".

Replies are listed 'Best First'.
Re^2: Unexpected behaviour with constant lists and foreach (and closures?)
by Crackers2 (Parson) on Jul 20, 2006 at 18:42 UTC

    I'm not exactly sure what you mean by counting loop. Are you saying that

    foreach $x (expr1..expr2) { blah }

    is implemented more like

    for ($y=expr1 ; $y<expr2 ; $y++) { $x=$y; blah }

    internally ?

      Indeed. It's probably implemented identically to for (my $x=expr1 ; $x<=expr2 ; $x++) { blah }

      Update: Changed "<" to "<=".

        It's not implemented like that at all. There's a specific set of ops meant for the range operator. It's much better than the longhand for(;;) which would be lots of more ops to dispatch.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊