views:

288

answers:

3

The following code gives an error message:

#!/usr/bin/perl -w

foreach my $var (0, 1, 2){
    $var += 2;
    print "$var\n";
}

Modification of a read-only value attempted at test.pl line 4.

Is there any way to modify $var? (I'm just asking out of curiosity; I was actually quite surprised to see this error message.)

+10  A: 

In a foreach $var (@list) construct, $var becomes aliased to the elements of the loop, in the sense that the memory address of $var would be the same address as an element of @list. So your example code attempts to modify read-only values, and you get the error message.

This little script will demonstrate what is going on in the foreach construct:

my @a = (0,1,2);
print "Before: @a\n";
foreach my $var (@a) {
  $var += 2;
}
print "After:  @a\n";

Before: 0 1 2
After:  2 3 4

Additional info: This item from perlsyn is easy to gloss over but gives the whole scoop:

Foreach loops

...

If any element of LIST is an lvalue, you can modify it by modifying VAR inside the loop. Conversely, if any element of LIST is NOT an lvalue, any attempt to modify that element will fail. In other words, the "foreach" loop index variable is an implicit alias for each item in the list that you're looping over.

mobrule
+3  A: 

Perl is complaining about the values which are constants, not the loop variable. The readonly value it's complaining about is your 0 because $var is an alias to it, and it's not stored in a variable (which is something that you can change). If you loop over an array or a list of variables, you don't have that problem.

brian d foy
+3  A: 

Figuring out why the following does not result in the same message will go a long way toward improving your understanding of why the message is emitted in the first place:

#!/usr/bin/perl

use strict;
use warnings;

for my $x ( @{[ 0, 1, 2 ]} ) {
    $x += 2;
    print $x, "\n";
}
Sinan Ünür