Глобальная переменная сбрасывает себя в подпрограмме - PullRequest
2 голосов
/ 21 сентября 2011

Простите за (возможно) глупый вопрос, но я возиться с этим кодом (на самом деле модель чего-то в более крупной программе), и что-то меня отбрасывает:

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' введена, она возвращается к предыдущему значению. Что мне здесь не хватает?

Как следствие, я не люблю здесь использовать глобальную переменную. Каков «правильный» способ сделать это, не делая $ g параметром для recurse ()?

Ответы [ 5 ]

7 голосов
/ 21 сентября 2011

my $g - локальная переменная, поэтому она не та, которую вы используете внутри recurse. Удаление my исправит это, хотя это все еще будет ужасный код.

Вы можете передать $g второй параметр в функцию resurse.

Примечание: use strict; твой друг;)

1 голос
/ 21 сентября 2011

Как сказал yi_H, my $g, который вы объявляете внутри цикла, является полностью отдельной переменной от $g, который используется recurse.Это работает в первый раз, потому что все переменные начинаются с undef, который в виде строки становится пустой строкой.Если вы попытаетесь распечатать $g внутри цикла после вызова recurse, вы увидите, что он все еще пуст.Использование строгого помогает выявить ошибки такого рода.Он может или не может поймать этот случай, в зависимости от того, как выглядит остальная часть вашей программы.

Самый простой способ справиться с этим - передать $g в качестве параметра.Однако иногда проще использовать рекурсивное замыкание.Обратите внимание, что Perl использует сборщик мусора с подсчетом ссылок, что означает, что он не может удалять структуры самоссылочных данных (включая замыкание, которое имеет ссылку на себя, чтобы он мог вызывать себя рекурсивно) до завершения всей программы.Я использую функцию ослабления из Scalar :: Util , чтобы избежать этого.$strongRef используется только для того, чтобы предотвратить сбор мусора перед тем, как мы закончим с ним.

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";
}
1 голос
/ 21 сентября 2011

Вы можете просто переместить определение g за пределы цикла и до определения функции.

0 голосов
/ 21 сентября 2011
Каков «правильный» способ сделать это без указания параметра $ ga для recurse ()

Легко, поскольку $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";
}
0 голосов
/ 21 сентября 2011

Как гласит старая поговорка, тяжелая работа окупается, а лень окупается сейчас .Вот как выглядит код теперь, когда я решил перестать быть ленивым и сделать это «правильно» (я также изменил $ 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";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...