Is there a way to modify foreach loop variable? - perl

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.)

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.

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.

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";
}

Related

Handling with two warnings of #ARGS

Small debug question I can't solve for some reason. Consider the following code:
use warnings;
my $flag = 0;
foreach my $i (0..scalar(#ARGV)) {
$data{$OPTION} .= $ARGV[$i]." " if($flag);
$flag = 1 if($ARGV[$i] =~ /$OPTION/);
undef $ARGV[$i] if($flag);
}
I get the following two warnings:
Use of uninitialized value within #ARGV in concatenation (.) or string at line 4
Use of uninitialized value in pattern match (m//) at line 5
I get the reason is that I undefine some value of #ARGV and then it tries to check it.
The way I do it like this is because I would like to 'cut' some of the data of #ARGV before using GetOpt module (which uses this array).
How to solve it?
Let's expand on those comments a bit.
Imagine #ARGV contains four elements. They will have the indexes 0, 1, 2 and 3 (as arrays in Perl are zero-based).
And your loop looks like this:
foreach my $i (0..scalar(#ARGV)) {
You want to visit each element in #ARGV, so you use the range operator (..) to generate a list of all those indexes. But scalar #ARGV returns the number of elements in #ARGV and that's 4. So your range is 0 .. 4. And there's no value at $ARGV[4] - so you get an "undefined value" warning (as you're trying to read past the end of an array).
A better way to do this is to use $#ARGV instead of scalar #ARGV. For every array variable in Perl (say #foo) you also get a variable (called $#foo) which contains the last index number in the array. In our case, that's 3 and your range (0 .. $#ARGV) now contains the integers 0 .. 3 and you no longer try to read past the end of the array and you don't get the "undefined value" warnings.
There's one other improvement I would suggest. Inside your loop, you only ever use $i to access an element from #ARGV. It's only used in expressions like $ARGV[$i]. In this case, it's probably better to skip the middle man and to iterate across the elements in the array, not the indexes.
I mean you can write your code like this:
foreach my $arg (#ARGV) {
$data{$OPTION} .= $arg . " " if($flag);
$flag = 1 if($arg =~ /$OPTION/);
undef $arg if($flag);
}
I think that's a little easier to follow.

Perl significance of $#_ variable

I see when I loop through elements of an array, and test $#_ , I get -1 for each element. I am hoping someone can explain what this variable does, and what it is used for most often.
Just like $#foo is the last existing index of array #foo, $#_ is the last existing index of array #_. If #_ is empty, $#_ is -1.
It sounds like you mean to use $_. $_ is aliased by foreach, map and grep loops to the element current being processed. while (<>) also sets $_ (as it gets rewritten to while (defined($_ = <>))). As a result, $_ is used as the default argument by many builtins (e.g. say).
# Print each element on its own line
say for #a;
is short for
# Print each element on its own line
say $_ for #a;
which is the terse form of
# Print each element on its own line
for my $ele (#a) {
say $ele;
}
I believe you mean $_ which is a special variable in Perl. It holds the current value while looping through a list element. For instance, below will print out each element of #foo, one at a time.
foreach (#foo) {
print $_;
}

For Loop and Lexically Scoped Variables

Version #1
use warnings;
use strict;
my $count = 4;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
if (not defined($count)) {
print "Count not defined\n";
}
else {
print "Count = $count\n";
}
This prints:
1
2
3
4
5
6
4
Why? Because the for loop creates its own lexically scoped version of $count inside its block.
Version #2
use warnings;
use strict;
my $count;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
if (not defined($count)) {
print "Count not defined\n";
}
else {
print "Count = $count\n";
}
1
2
3
4
5
6
Count not defined
Whoops! I wanted to capture the exit value of $count, but the for loop had it's own lexically scoped version of $count!. I just had someone spend two hours trying to track down this bug.
Version #3
use warnings;
use strict;
for $count (1..8) {
print "Count = $count\n";
last if ($count == 6);
}
print "That's all folks!\n";
This gives me the error Global symbol "$count" requires explicit package name at line 5. But, I thought $count was automatically lexically scoped inside the for block. It seems like that only occurs when I've already declared a lexically scoped version of this variable elsewhere.
What was the reason for this behavior? Yes, I know about Conway's dictate that you should always use my for the for loop variable, but the question is why was the Perl interpretor designed this way.
In Perl, assignment to the variable in the loop is always localized to the loop, and the loop variable is always an alias to the looped over value (meaning you can change the original elements by modifying the loop variable). This is true both for package variables (our) and lexical variables (my).
This behavior is closest to that of Perl's dynamic scoping of package variables (with the local keyword), but is also special cased to work with lexical variables (either declared in the loop or before hand).
In no case though does the looped over value persist in the loop variable after the loop ends. For a loop scoped variable, this is fairly intuitive, but for variables with scope beyond the loop, the behavior is analogous to a value localized (with local) inside of a block scope created by the loop.
for our $val (1 .. 10) {...}
is equivalent to:
our $val;
my #list = 1 .. 10;
my $i = 0;
while ($i < #list) {
local *val = \$list[$i++];
# loop body
}
In pure perl it is not possible to write the expanded lexical version, but if a module like Data::Alias is used:
my $val;
my #list = 1 .. 10;
my $i = 0;
while ($i < #list) {
alias $val = $list[$i++];
# loop body
}
Actually, in version #3 the variable is "localized" as opposed to lexically scoped.
The "foreach" loop iterates over a normal list value and sets the
variable VAR to be each element of the list in turn. If the variable
is preceded with the keyword "my", then it is lexically scoped, and is
therefore visible only within the loop. Otherwise, the variable is
implicitly local to the loop and regains its former value upon exiting
the loop. If the variable was previously declared with "my", it uses
that variable instead of the global one, but it's still localized to
the loop. This implicit localisation occurs only in a "foreach" loop.
In any case, you will not be able to access the loop variable from that stlye of for-loop outside the loop. But you could use the other style (C-style) for-loop:
my $count;
for ($count=1; $count <= 8; $count++) {
last if $count == 6;
}
... # $count is now 6.
Why? Because the for loop creates its own lexically scoped version of $count inside its block.
This is wrong. If you had written for my $count (...) { ... } it would be true, but you didn't. Instead, if $count is already a global, it's localized -- the global that already exists is set to new values during the execution of the loop, and set back when it's done. The difference should be clear from this:
our $x = "orig";
sub foo {
print $x, "\n";
}
foo();
for $x (1 .. 3) {
foo();
}
for my $x (1 .. 3) {
foo();
}
The output is
orig
1
2
3
orig
orig
orig
The first for loop, without my, is changing the value of the global $x that already exists. The second for loop, with my, is creating a new lexical $x that isn't visible outside the loop. They're not the same.
This is also why example #3 fails -- since there isn't a lexical $count in scope and you haven't declared that you intend to touch the package global $count, strict 'vars' stops you in your tracks. It's not really behaving any differently for a for loop than anything else.

What's the difference between 'for' and 'foreach' in Perl?

I see these used interchangeably. What's the difference?
There is no difference. From perldoc perlsyn:
The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity.
I see these used interchangeably.
There is no difference other than that of syntax.
Ever since its introduction in perl-2.0, foreach has been synonymous with for. It's a nod to the C shell's foreach command.
In my own code, in the rare case that I'm using a C-style for-loop, I write
for (my $i = 0; $i < $n; ++$i)
but for iterating over an array, I spell out
foreach my $x (#a)
I find that it reads better in my head that way.
Four letters.
They're functionally identical, just spelled differently.
From http://perldoc.perl.org/perlsyn.html#Foreach-Loops
The foreach keyword is actually a synonym for the for keyword, so you
can use either. If VAR is omitted, $_ is set to each value.
# Perl's C-style
for (;;) {
# do something
}
for my $j (#array) {
print $j;
}
foreach my $j (#array) {
print $j;
}
However:
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.
The foreach keyword is actually a synonym for the for keyword, so you can use foreach for readability or for for brevity. (Or because the Bourne shell is more familiar to you than csh, so writing for comes more naturally.) If VAR is omitted, $_ is set to each value.
There is a subtle difference (http://perldoc.perl.org/perlsyn.html#Foreach-Loops) :
The foreach loop iterates over a normal list value and sets the variable VAR to be each element of the list in turn. If the variable is preceded with the keyword my, then it is lexically scoped, and is therefore visible only within the loop. Otherwise, the variable is implicitly local to the loop and regains its former value upon exiting the loop. If the variable was previously declared with my, it uses that variable instead of the global one, but it's still localized to the loop. This implicit localization occurs only in a foreach loop.
This program :
#!/usr/bin/perl -w
use strict;
my $var = 1;
for ($var=10;$var<=10;$var++) {
print $var."\n"; # print 10
foo(); # print 10
}
print $var."\n"; # print 11
foreach $var(100) {
print $var."\n"; # print 100
foo(); # print 11 !
}
sub foo {
print $var."\n";
}
will produce that :
10
10
11
100
11
In case of the "for" you can use the three steps.
1) Initialization
2) Condition Checking
3) Increment or decrement
But in case of "foreach" you are not able increment or decrement the value. It always take the increment value as 1.

What perl code samples can lead to undefined behaviour?

These are the ones I'm aware of:
The behaviour of a "my" statement modified with a statement modifier conditional or loop construct (e.g. "my $x if ...").
Modifying a variable twice in the same statement, like $i = $i++;
sort() in scalar context
truncate(), when LENGTH is greater than the length of the file
Using 32-bit integers, "1 << 32" is undefined. Shifting by a negative number of bits is also undefined.
Non-scalar assignment to "state" variables, e.g. state #a = (1..3).
One that is easy to trip over is prematurely breaking out of a loop while iterating through a hash with each.
#!/usr/bin/perl
use strict;
use warnings;
my %name_to_num = ( one => 1, two => 2, three => 3 );
find_name(2); # works the first time
find_name(2); # but fails this time
exit;
sub find_name {
my($target) = #_;
while( my($name, $num) = each %name_to_num ) {
if($num == $target) {
print "The number $target is called '$name'\n";
return;
}
}
print "Unable to find a name for $target\n";
}
Output:
The number 2 is called 'two'
Unable to find a name for 2
This is obviously a silly example, but the point still stands - when iterating through a hash with each you should either never last or return out of the loop; or you should reset the iterator (with keys %hash) before each search.
These are just variations on the theme of modifying a structure that is being iterated over:
map, grep and sort where the code reference modifies the list of items to sort.
Another issue with sort arises where the code reference is not idempotent (in the comp sci sense)--sort_func($a, $b) must always return the same value for any given $a and $b.