全局变量在子程序中重置自身

时间:2011-09-20 22:01:02

标签: perl recursion scope

请原谅我(可能)愚蠢的问题,但我正在搞乱这个代码(实际上是一个更大程序中的某个模型),有些东西让我失望:

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

'for'循环的第一次迭代工作正常。然而,在随后的迭代中,我遇到了一个问题。如您所见,在调用递归函数之前,全局变量$ g在'for'循环中首先被重置。使用调试器,我可以看到在调用函数之前$ g被设置回''。但是,只要输入函数'recurse',它就会返回到先前的值。我在这里缺少什么?

作为必然结果,我不喜欢在这里使用全局变量。如果没有为$ recurse()创建$ g参数,那么“正确”的方法是什么?

5 个答案:

答案 0 :(得分:7)

my $g是一个局部变量,因此它与recurse中使用的变量不同。删除my将解决这个问题,尽管它仍然是一个丑陋的代码。

您可以将$g第二个参数传递给resurse函数。

注意:use strict;是你的朋友;)

答案 1 :(得分:1)

您可以简单地将g的定义移出循环之外和函数定义之前。

答案 2 :(得分:1)

正如yi_H所说,你在循环中声明的my $g是与$g正在使用的recurse完全独立的变量。它第一次起作用,因为所有变量都以undef开头,作为字符串变为空字符串。如果您在调用$g后尝试在循环内打印recurse,则会看到它仍为空。使用strict有助于捕获此类错误。它可能会也可能不会发生这种情况,具体取决于程序的其余部分。

处理此问题的最简单方法是将$g作为参数传递。但是,有时使用递归闭包更容易。请注意,Perl使用引用计数垃圾收集器,这意味着它不能删除自引用数据结构(包括具有对自身的引用的闭包,以便它可以递归地调用自身),直到整个程序退出。我使用weaken中的Scalar::Util函数来避免这种情况。 $strongRef仅用于防止coderef在我们完成之前被垃圾收集。

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

但是,在这种特殊情况下,我可能只是直接填充$str而不是返回值:

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

答案 3 :(得分:0)

正如那句老话说的那样,努力工作得到了回报,但懒惰现在已经付出了 。这是代码看起来像我现在决定停止懒惰,并以'正确'方式执行(我还将$ g更改为数组,以避免笨拙的前导分隔符):

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

答案 4 :(得分:0)

什么是'正确'的方法,没有为$ recurse()
创建$ g参数

很简单,因为根本不需要$g

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