Reputation: 119
#!/usr/bin/env perl
use strict;
use warnings FATAL => qw ( all );
my ( $func, @list, $num );
$func = sub {
print $num // "undef"; print "\n";
};
@list = ( 1,2,3 );
foreach $num ( @list ) {
$func->();
};
This piece of perl prints
undef
undef
undef
instead of
1
2
3
The $func
routine can see @list
, so why not $num
?
Upvotes: 2
Views: 59
Reputation: 29854
I could quote perlsyn, but perlsyn does not appear to say clearly what is the case. The variable is not "localized", and there is nothing that "regains" its value upon exiting the loop. The outer variable is hidden when you declare the loop variable, and visible after you're out of the loop.
It implicitly hides the variable in the outer scope, which retains its value.
The output from this code scrap will illustrate:
use 5.022;
use strict;
use warnings FATAL => qw ( all );
use Data::Dumper;
my ( $func, @list, $num );
$func = sub {
print $num // "undef"; print "\n";
};
$num = 101;
$func->();
my $np = \$num;
print Data::Dumper->Dump( [ $np ], [ '*np' ] );
@list = ( 1,2,3 );
foreach $num ( @list ) {
print Data::Dumper->Dump( [ $num ], [ '*num' ] );
print Data::Dumper->Dump( [ $np ], [ '*np' ] );
my $np2 = \$num;
print Data::Dumper->Dump( [ $np2 ], [ '*np2' ] );
print Data::Dumper->Dump( [ $np ], [ '*np' ] );
$$np++;
$func->();
}
Which outputs this:
101
$np = \101;
$num = 1;
$np = \101;
$np2 = \1;
$np = \101;
102
$num = 2;
$np = \102;
$np2 = \2;
$np = \102;
103
$num = 3;
$np = \103;
$np2 = \3;
$np = \103;
104
Thus, inside the loop, the symbol $num
simply does not refer to the memory pointed to by $np
, but instead at the memory pointed to by $np2
.
Upvotes: 0
Reputation: 119
Well, guess the reason is this, from the docs:
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.
which means I should not use the foreach loop i guess, but for or while ..
Upvotes: 0
Reputation: 67900
You are accessing a localized version of the $num
variable, like Sobrique says. What you intended was to use a reference to the variable $num
. Which is what I show here:
my ( $func, @list, $num );
$func = sub {
print $$num // "undef"; print "\n";
};
@list = ( 1,2,3 );
foreach ( @list ) {
$num = \$_;
$func->();
};
You can also use the global version of the variable, instead of the lexical:
my ( $func, @list, $num );
$func = sub {
print $main::num // "undef"; print "\n";
};
@list = ( 1,2,3 );
foreach $main::num ( @list ) {
$func->();
};
But this is a silly way to encapsulate this. You should not use global variables inside subroutines. This is not good practice. Instead, pass the value to the sub and access it via the @_
variable (in this case by the first array index $_[0]
):
my $func = sub {
print $_[0] // "undef"; print "\n";
};
my @list = ( 1,2,3 );
for ( @list ) {
$func->($_);
};
I also fixed some idiomatic coding style above.
Upvotes: 1
Reputation: 53478
Because foreach
loops implicitly localise their iterator variables.
See: perlsyn
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.
But really - this doesn't come up often, because it's really bad form to do any sort of messing around with a loop iterator from outside. Narrow down your scope, and pass variables around to avoid bugs and troubleshooting pain.
Upvotes: 4