Discussion:
A bit of perl I don't understand
Philip Davies
2018-08-13 19:29:52 UTC
Permalink
Hi,

I'm trying to understand why there is a difference in output between the two loops in the sample code below.

#!/usr/bin/perl

use strict;
use warnings;

my @array = (1, 3, 5, 7, 11);
my $number = 6;

#---------------------------------------------------------------------
my $index = undef;

LOOP1: for ($index = 0; $index <= $#array; $index++)
{
print $index," - ",$array[$index],"\n";
last LOOP1 if ($number < $array[$index]);
}
print "index = ",$index,"\n";

print STDERR "\n";
#---------------------------------------------------------------------
$index = undef;

LOOP2: foreach $index (0..$#array)
{
print $index," - ",$array[$index],"\n";
last LOOP2 if ($number < $array[$index]);
}
print "index = ",$index,"\n";

#---------------------------------------------------------------------

exit 0;


When run the output is:-

# ./test.pl

0 - 1
1 - 3
2 - 5
3 - 7
index = 3

0 - 1
1 - 3
2 - 5
3 - 7
Use of uninitialized value $index in print at ./test.pl line 29.
index =

Having declared $index outside both loops, I would have expected that $index would retain its last value in the second loop. Any pointers to where in the perl documentation I should be looking would be most appreciated.

Cheers
Phil
Dermot Paikkos
2018-08-13 19:36:01 UTC
Permalink
Hi,

A quick look suggests that the Loop1 returns index=3 because that as far as you got through $array[$index], e.g. you got to the 3rd item before terminating.
Loop2 returns undef because $index is defined in the `for` loop and so it is out of scope by the time you print it.

Hope that helps (more importantly, hope I’m not incorrect)
Dermot.

From: Philip Davies
Sent: 13 August 2018 20:30
To: London PM
Subject: A bit of perl I don't understand

Hi,

I'm trying to understand why there is a difference in output between the two loops in the sample code below.

#!/usr/bin/perl

use strict;
use warnings;

my @array = (1, 3, 5, 7, 11);
my $number = 6;

#---------------------------------------------------------------------
my $index = undef;

LOOP1: for ($index = 0; $index <= $#array; $index++)
{
print $index," - ",$array[$index],"\n";
last LOOP1 if ($number < $array[$index]);
}
print "index = ",$index,"\n";

print STDERR "\n";
#---------------------------------------------------------------------
$index = undef;

LOOP2: foreach $index (0..$#array)
{
print $index," - ",$array[$index],"\n";
last LOOP2 if ($number < $array[$index]);
}
print "index = ",$index,"\n";

#---------------------------------------------------------------------

exit 0;


When run the output is:-

# ./test.pl

0 - 1
1 - 3
2 - 5
3 - 7
index = 3

0 - 1
1 - 3
2 - 5
3 - 7
Use of uninitialized value $index in print at ./test.pl line 29.
index =

Having declared $index outside both loops, I would have expected that $index would retain its last value in the second loop. Any pointers to where in the perl documentation I should be looking would be most appreciated.

Cheers
Phil
ɯɐpɐ
2018-08-13 19:39:22 UTC
Permalink
http://perldoc.perl.org/perlsyn.html#Foreach-Loops

The foreach loop iterates over a normal list value and sets the scalar
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.

Hope this helps :-)
Post by Dermot Paikkos
Hi,
A quick look suggests that the Loop1 returns index=3 because that as far
as you got through $array[$index], e.g. you got to the 3rd item before
terminating.
Loop2 returns undef because $index is defined in the `for` loop and so it
is out of scope by the time you print it.
Hope that helps (more importantly, hope I’m not incorrect)
Dermot.
*Sent: *13 August 2018 20:30
*Subject: *A bit of perl I don't understand
Hi,
I'm trying to understand why there is a difference in output between the
two loops in the sample code below.
#!/usr/bin/perl
use strict;
use warnings;
my $number = 6;
#---------------------------------------------------------------------
my $index = undef;
LOOP1: for ($index = 0; $index <= $#array; $index++)
{
print $index," - ",$array[$index],"\n";
last LOOP1 if ($number < $array[$index]);
}
print "index = ",$index,"\n";
print STDERR "\n";
#---------------------------------------------------------------------
$index = undef;
LOOP2: foreach $index (0..$#array)
{
print $index," - ",$array[$index],"\n";
last LOOP2 if ($number < $array[$index]);
}
print "index = ",$index,"\n";
#---------------------------------------------------------------------
exit 0;
When run the output is:-
# ./test.pl
0 - 1
1 - 3
2 - 5
3 - 7
index = 3
0 - 1
1 - 3
2 - 5
3 - 7
Use of uninitialized value $index in print at ./test.pl line 29.
index =
Having declared $index outside both loops, I would have expected that
$index would retain its last value in the second loop. Any pointers to
where in the perl documentation I should be looking would be most
appreciated.
Cheers
Phil
--
Adam
Christian Jaeger
2018-08-14 02:01:17 UTC
Permalink
This made me wonder about the details.
Post by ɯɐpɐ
http://perldoc.perl.org/perlsyn.html#Foreach-Loops
The foreach loop iterates over a normal list value and sets the scalar
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 last sentence isn't properly describing what Perl seems to be
doing: what really seems to happen is that it uses another, fresh,
lexical variable of the same name.

I.e. if there's a lexical variable in scope with the name that you're
using in foreach, then it implicitly adds the "my" keyword anyway that
careful people would manually add (i.e. behaves exactly the same).
Otherwise, it "local"izes the package global of that name.

The docs make it sound like it uses "local" on the lexical
variable--Perl's compiler is prohibiting this for explicit use of
"local". That might be the reason why it's really using "my"
underneath. Maybe the internals were changed at one point to do that
for some consistency, and the wording in the docs not updated to
reflect it?


Here's the program that led me to that conclusion:

use strict; use warnings;

my @array = (1, 3, 5, 7, 11);
my $number = 6;
my $index = "notset";

$main::index = "mainnotset";

sub set_index {
($index)=@_;
}

sub show_main_index {
print "main::index = ",$main::index,"\n";
}

my $get_inner_index;

# try with an explicit "my" as well
foreach $index (0..$#array)
{
show_main_index;
#set_index($index); # try to uncomment this
$get_inner_index= sub { $index };
print $index," - ",$array[$index],"\n";
last if ($number < $array[$index]);
}
print "index = ",$index,"\n";
print "get_inner_index = ",&$get_inner_index,"\n";
show_main_index;


# The above shows that foreach $index and foreach my $index are doing
# the exact same thing (i.e. use *another* lexical $index variable).

# Now, is actual "local"ization of a lexical possible? No:

# sub foo {
# my ($x)= @_;
# {
# local $x= $x+1; # => compiler error: Can't localize lexical variable $x
# print "inner x=$x\n";
# }
# print "outer x=$x\n";
# }

# foo 1;
mikey
2018-08-14 05:29:15 UTC
Permalink
Perhaps it helps to realise that the "foreach" version looping over a list
(which incidentally can also be written using just "for") is not assigning
each value to the iterator but aliasing that value. That is why assigning
to it modifies the values in the list, like so:

use feature 'say';

my @a = 0..5;
my $i;

foreach $i (@a) {
$i = $i*2 + 1;
}

say $i // 'undef';

local $" = ', ';
say "(@a)";

# undef
# (1, 3, 5, 7, 9, 11)
Post by Christian Jaeger
This made me wonder about the details.
Post by ɯɐpɐ
http://perldoc.perl.org/perlsyn.html#Foreach-Loops
The foreach loop iterates over a normal list value and sets the scalar
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
Post by ɯɐpɐ
visible only within the loop.
** Otherwise, the variable is implicitly local to the loop and regains
its
Post by ɯɐpɐ
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 last sentence isn't properly describing what Perl seems to be
doing: what really seems to happen is that it uses another, fresh,
lexical variable of the same name.
I.e. if there's a lexical variable in scope with the name that you're
using in foreach, then it implicitly adds the "my" keyword anyway that
careful people would manually add (i.e. behaves exactly the same).
Otherwise, it "local"izes the package global of that name.
The docs make it sound like it uses "local" on the lexical
variable--Perl's compiler is prohibiting this for explicit use of
"local". That might be the reason why it's really using "my"
underneath. Maybe the internals were changed at one point to do that
for some consistency, and the wording in the docs not updated to
reflect it?
use strict; use warnings;
my $number = 6;
my $index = "notset";
$main::index = "mainnotset";
sub set_index {
}
sub show_main_index {
print "main::index = ",$main::index,"\n";
}
my $get_inner_index;
# try with an explicit "my" as well
foreach $index (0..$#array)
{
show_main_index;
#set_index($index); # try to uncomment this
$get_inner_index= sub { $index };
print $index," - ",$array[$index],"\n";
last if ($number < $array[$index]);
}
print "index = ",$index,"\n";
print "get_inner_index = ",&$get_inner_index,"\n";
show_main_index;
# The above shows that foreach $index and foreach my $index are doing
# the exact same thing (i.e. use *another* lexical $index variable).
# sub foo {
# {
# local $x= $x+1; # => compiler error: Can't localize lexical
variable $x
# print "inner x=$x\n";
# }
# print "outer x=$x\n";
# }
# foo 1;
Philip Davies
2018-08-13 20:12:36 UTC
Permalink
Hi,

my $index is defined before both loops so I would expect it to be global. I would expect that the value for $index be visible after the loops are evaluated. The value of $index reset to undefined before the second loop. It looks more like a scoping error.

Cheers
Phil
Post by Dermot Paikkos
Hi,
A quick look suggests that the Loop1 returns index=3 because that as far as you got through $array[$index], e.g. you got to the 3rd item before terminating.
Loop2 returns undef because $index is defined in the `for` loop and so it is out of scope by the time you print it.
Hope that helps (more importantly, hope I’m not incorrect)
Dermot.
ɯɐpɐ
2018-08-13 20:30:55 UTC
Permalink
as per the link provided and the highlighted sentence, the loop variable is
locally scoped within the block. You can expect it to be something
different but unfortunately that's not how it works.

Most code you see will be of the form

for my $index (0..$#array) { ... }

which discourages misinterpretation of the scoping as the 'my' reinforces
the block scopeyness.

As with most higher-level languages I'd recommend looping over the items in
an array themselves and trying not to think about them in terms of indexes
at all - the loop constructs will alias your variable to each item in turn.
If you *really* need to know the index then I'd recommend tracking it
separately, eg

my @scores = (1, 3, 5, 7, 11);
my $max = 6;
my $iteration = 1;
for my $score ( @scores ) {
print "$iteration - $score\n";
last if $score > $max;
$iteration++;
}
print "exited after item $iteration\n";

1 - 1
2 - 3
3 - 5
4 - 7
exited after item 4

HTH :-)
Post by Philip Davies
Hi,
my $index is defined before both loops so I would expect it to be global.
I would expect that the value for $index be visible after the loops are
evaluated. The value of $index reset to undefined before the second loop.
It looks more like a scoping error.
Cheers
Phil
Post by Dermot Paikkos
Hi,
A quick look suggests that the Loop1 returns index=3 because that as far
as you got through $array[$index], e.g. you got to the 3rd item before
terminating.
Post by Dermot Paikkos
Loop2 returns undef because $index is defined in the `for` loop and so
it is out of scope by the time you print it.
Post by Dermot Paikkos
Hope that helps (more importantly, hope I’m not incorrect)
Dermot.
--
Adam
Loading...