Perl 嵌套子对象前面的神秘*

Perl 嵌套子对象前面的神秘*,perl,typeglob,Perl,Typeglob,事实前*的确切功能/目的是什么?如何等效地编写 sub fact { my ($n) = @_; local *_fact = sub { my ($n, $prod) = @_; return $prod if $n == 0; return _fact($n-1, $n*$prod); }; return _fact($n, 1); } fact($n); 检查 上述示例应使用匿名子例程/闭包编写: sub fact

事实前*的确切功能/目的是什么?如何等效地编写

sub fact {
   my ($n) = @_;

   local *_fact = sub {
       my ($n, $prod) = @_;
       return $prod if $n == 0;
       return _fact($n-1, $n*$prod);
   };

   return _fact($n, 1);
}

fact($n);
检查

上述示例应使用匿名子例程/闭包编写:

sub fact {
   my ($n) = @_;

   my $_fact;
   $_fact = sub {
       my ($n, $prod) = @_;
       return $prod if $n == 0;
       return __SUB__->($n-1, $n*$prod);
   };

   return $_fact->($n, 1);
}

它被称为
typeglob
,用于创建表别名。请参阅以获取更多信息。

这似乎是一种通过将代码引用分配给名为
\u fact
的typeglob,然后以伪递归方式调用它来创建闭包的笨拙尝试。(注意:typeglob是具有特定名称的所有变量的容器)

写这篇文章的一种几乎相当(而且更标准)的方式是:

sub fact {
   my ($n) = @_;

   my $_fact;

   $fact = sub { .... }; # Assigning code-ref to scalar variable.

   return $_fact->($n, 1); # Note the arrow syntax to deref the code-ref
}
…但是,正如有人善意地指出的,它有一个内存泄漏。。。所以,我说干脆把闭包全部扔掉,这样写:

sub fact {
   my($n,$prod) = @_;

   return((defined $prod) ? (($n == 0) ? $prod : fact($n-1, $n * $prod)) : fact($n,1));
}

(记住,唯一比无限递归更糟糕的是…无限递归)

理想情况下,函数的作者会希望使用

sub fact {
   my ($n) = @_;

   my $_fact; $_fact = sub {
       my ($n, $prod) = @_;
       return $prod if $n == 0;
       return $_fact->($n-1, $n*$prod);
   };

   return $_fact->($n, 1);
}
不幸的是,它存在内存泄漏。anon sub有一个对
$\u fact
的引用,其中包含对匿名sub的引用。
$\u fact
需要清除才能在退出时断开引用

sub fact {
   my ($n) = @_;

   my $_fact;
   $_fact = sub {
       my ($n, $prod) = @_;
       return $prod if $n == 0;
       return $_fact->($n-1, $n*$prod);
   };

   my $rv;
   my $e = eval { $rv = $_fact->($n, 1); 1 } ? undef : ($@ || 'Unknown');
   $_fact = undef;
   die $e if $e
   return $rv;       
}
但那太难看了!避免此问题的一种方法是使用。避免该问题的一种更简单的方法是将代码引用存储在包变量中,而不是存储在词法变量中(因为sub只捕获词法变量)。这就是你发布的代码所做的。记住

*_fact = sub { ...  };
基本上是的运行时版本

sub _fact { ... }
两者都将子项分配给符号\u fact的代码槽

也就是说,5.16引入了一个更好的解决方案:

use feature qw( current_sub );

sub fact {
   my ($n) = @_;

   my $_fact = sub {
       my ($n, $prod) = @_;
       return $prod if $n == 0;
       return __SUB__->($n-1, $n*$prod);
   };

   return $_fact->($n, 1);
}

“在事实面前,*的确切功能/目的是什么?”它没有泄漏,但这是一个糟糕的解决方案。它删除了尾部递归,这显然是子代码设计中的一个指导因素。
$n$n*事实($n-1):否则1
就足够了。消除尾部递归使得它到处都是低效的。将外部子对象更改为以
my$rv=$\u事实(…)结尾;未定义$u事实$rv
更合适。我更进一步,添加了一个
eval
。这里可能不需要它,因为我没有预见到任何运行时错误,但我将其包括在内,因为这显然是一个学习练习。在Perl中对阶乘使用递归是非常浪费的。@ikegami,实际上,这段代码没有内存泄漏。相反,
$\u fact
在子系统中不可访问,因此不能用于递归调用。saw Y combinator。单击+1。@ikegami您能解释一下为什么需要评估部分吗?这仅仅是为了防止抛出的错误导致内存泄漏吗?@Nate Glenn,即使出现错误也要打破内存周期(除非你知道程序无论如何都会退出。)这里可能不需要
eval
,因为我没有预见到任何运行时错误,但我将其包括在内,因为这显然是一个学习练习。在Perl中对阶乘使用递归是非常浪费的。