perltutorial
imp
You tried, directly or indirectly, to change the value of a
constant. This type of error frequently occurs at a distance and is difficult to trace. The magic of $_ is often involved, but is not responsible.
<p>
<p>Common causes:
<ol>
<li>
<a href="#loop_lvalue">
Treating a loop variable as an lvalue
</a>
</li>
<li>
<a href="#map_grep">
Modifying $_ inside foreach, map or grep
</a>
</li>
<li>
<a href="#param_lvalue">
Modifying elements of @_ directly
</a>
</li>
<li>
<a href="#sort_modify">
Modifying $a or $b inside sort
</a>
</li>
<li>
<a href="#sort_vivify">
Autovivifying $a or $b inside sort
</a>
</li>
<li><a href="#default_unlocalized">
Modifying an unlocalized $_
</a></li>
</ol>
<p>
<a name="loop_lvalue">
<h4>Treating a loop variable as an lvalue</h4>
</a>
<code>
for my $x (1,2) {
$x++;
}
</code>
In this example $x is aliased to the constant '1', so when the loop body attempts to increment $x an error is triggered. See <a href="#lists_with_constants">Lists With Constant Values</a> for more details.
<a name="map_grep">
<h4>
Modifying $_ inside [doc://perlsyn#Foreach-Loops-for-foreach|foreach], [doc://map] or [doc://grep]
</h4>
</a>
<code>
for (1,2) {
chomp;
}
for ("foo", @list) {
s/foo/bar/;
}
@array = map { $_++ } (1,2);
@array = grep { $_++ } (1,2);
</code>
In all of these examples $_ is aliased to a constant, and when the loop body attempts to modify $_ an error is triggered. See <a href="#lists_with_constants">Lists With Constant Values</a> for more details.
<a name="param_lvalue">
<h4>Modifying elements of @_ directly
</h4>
</a>
<code>
sub incr {
$_[0]++;
}
my $n = 1;
incr($n); # good
incr(1); # bad
</code>
Modifying elements of <code>@_</code> directly allows you to
modify the variable that was passed to the function. For example, in the above example <i>$n</i> is now 2. But an error will occur when a constant is passed, as in the second call.
<a name="sort_modify">
<h4>Modifying $a or $b inside [doc://sort]</h4>
</a>
<code>
@array = sort { $a++ } (1,2);
</code>
It is permissible (but ill-advised) to modify $a and $b within sort. However, modifying a constant that is aliased to $a or $b is still an error.
<a name="sort_vivify">
<h4>Autovivifying $a or $b inside [doc://sort]</h4>
</a>
<code>
my @bad;
$bad[0] = [1];
$bad[2] = [2];
@bad = sort {$a->[0] <=> $b->[0]} @bad;
</code>
The variables $a and $b are aliased to each item in the list being sorted, and as such modifying them is possible - but will cause an error if the current element is unmodifiable. A common cause of this is sorting an array of references when where the list has a gap. In this situation $a will be undef, and autovivification by dereferencing will trigger an error.
<a name="default_unlocalized">
<h4>Modifying an unlocalized $_</h4>
</a>
<code>
for (1,2) {
my $data = prompt_user();
}
sub prompt_user {
print "Enter a number\n";
while (<STDIN>) {
# Do stuff
}
}
</code>
This example will cause an error because the [doc://perlsyn#Foreach-Loops-for-foreach|for] loop aliases $_ to the literal '1', and then calls prompt_user which attempts to read a line from STDIN and store it in $_ - which is still aliased to '1'.
<p>
The error will also occur in this simplified scenario:
<code>
for (1,2) {
while (<STDIN>) {
}
}
</code>
<h3>Guidelines to avoid read-only errors:</h3>
<ol>
<li>Don't treat loop variables as a lvalue if there is any chance a constant value will be included</li>
<li>Don't modify an unlocalized $_</li>
<li>Don't modify $_ inside map or grep</li>
<li>Don't modify $a or $b inside sort</li>
<li>Don't dereference $a or $b inside sort without checking that they exist and are references</li>
<li>Don't use $_ for the loop variable unless it is a very trivial loop</li>
<li>Don't modify elements of @_ directly</li>
</ol>
<hr>
<h3>Notes</h3>
<a name="lists_with_constants">
<h4>Lists With Constant Values</h4>
</a>
Within the context of this document the important thing to understand is what expressions result in modifiable objects.
The following expressions have constants in them:
<code>
$_++ for (1,2);
$_++ for (1,@array);
@array = map {$_++} (1,@array);
</code>
And the following are safe:
<code>
my @array = (1,2);
for (@array) {
$_++;
}
</code>
<code>
my ($x,$y) = (1,2);
for ($x,$y) {
$_++;
}
</code>
For an explanation of lists versus arrays I recommend the following:
<ul>
<li>[id://451421]</li>
<li>[href://http://japhy.perlmonk.org/articles/pm/2000-02.html|"List" Is a Four-Letter Word]</li>
</ul>