Global variable resetting itself in subroutine
Forgive me for the (probably) stupid question, but I'm messing around with this code (actually a model of something in a larger program), and something is throwing me off:
sub recurse {
my $m = shift;
$g .= "::" . $m;
if ($m == 0) { return $g; }
else { $m--; recurse ($m); }
}
for ($i = 0; $i < 3; $i++)
{
my $g = '';
$str = recurse (10);
print $str . "\n";
}
The first iteration of the 'for' loop works fine. On subsequent iterations, however, I am having an issue. As you can see, the global variable $g is reset first thing in the 'for' loop before the recursive function is called. With the debugger, I can see that $g is set back to '' b开发者_开发问答efore the function is called. However, as soon as the function 'recurse' is entered, it goes back to the previous value. What am I missing here?
As a corollary, I don't like using a global variable here. What is the 'correct' way to do it without making $g a parameter for recurse()?
my $g
is a local variable so it's not the same one you use inside recurse
. Removing my
will fix that, though it's still going to be an ugly code.
You can pass $g
a second parameter to the resurse
function.
Note: use strict;
is your friend ;)
You could simply move the definition of g
outside the loop and before the function definition.
As yi_H said, the my $g
you declare inside the loop is a completely separate variable from the $g
that recurse
is using. It works the first time because all variables start out as undef
, which as a string becomes the empty string. If you tried printing out $g
inside the loop after calling recurse
, you'd see it was still empty. Using strict helps catch this sort of error. It might or might not have caught this case, depending on what the rest of your program looks like.
The easiest way to handle this is to pass $g
as a parameter. However, sometimes it's easier to use a recursive closure. Note that Perl uses a reference-count garbage collector, which means it can't delete self-referential data structures (including a closure that has a reference to itself so it can call itself recursively) until the entire program exits. I use the weaken function from Scalar::Util to avoid that. $strongRef
is used only to keep the coderef from being garbage collected before we're done with it.
use Scalar::Util 'weaken';
for (my $i = 0; $i < 3; $i++)
{
my $g = '';
my $recurse;
my $strongRef = $recurse = sub {
my $m = shift;
$g .= "::" . $m;
if ($m == 0) { return $g; }
else { $m--; $recurse->($m); }
};
weaken($recurse); # Prevent memory leak
my $str = $recurse->(10);
print $str . "\n";
}
However, in this particular case, I'd probably just have the closure fill in $str
directly instead of returning a value:
for (my $i = 0; $i < 3; $i++)
{
my $str = '';
my $recurse;
my $strongRef = $recurse = sub {
my $m = shift;
$str .= "::" . $m;
if ($m-- > 0) { $recurse->($m); }
};
weaken($recurse); # Prevent memory leak
$recurse->(10);
print $str . "\n";
}
As the old saying goes, hard work pays off, but laziness pays off now. Here's what the code looks like now that I decided to stop being lazy, and do it the 'right' way (I also changed $g to an array, to avoid awkward leading separators):
use strict;
sub recurse {
my $m = shift;
my @g = qw();
@g = @{$_[0]} if $_[0];
push (@g, $m);
if ($m == 0) { return @g; }
else { $m--; recurse ($m, \@g); }
}
for ( my $i = 0; $i < 3; $i++)
{
my @str = recurse (10);
print join('::', @str) . "\n";
}
What is the 'correct' way to do it without making $g a parameter for recurse()
Easy, since $g
is not needed at all.
sub recurse {
my $m = shift;
if ($m == 0) {
return "::" . $m;
} else {
return "::" . $m . recurse($m-1);
}
}
for ($i = 0; $i < 3; $i++)
{
print recurse(10) . "\n";
}
精彩评论