_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 $_fact->($n-1, $n*$prod);
};
return $_fact->($n, 1);
}
不幸的是,这有内存泄漏。anon sub 具有对 的引用$_fact
,其中包含对匿名 sub 的引用。$_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;
}
但这很丑陋!避免该问题的一种方法是使用Y 组合器。避免该问题的一种更简单的方法是将代码引用存储在包变量而不是词法变量中(因为只有词法变量被 subs 捕获)。这就是您发布的代码的作用。请记住,
*_fact = sub { ... };
基本上是一个运行时版本
sub _fact { ... }
两者都将 sub 分配给 symbol 的 CODE 槽_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);
}
上面的示例应该使用匿名子例程/闭包编写:
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_fact
然后以伪递归方式调用它来创建闭包的一种时髦尝试。(注意: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));
}
(请记住,唯一比无限递归更糟糕的是......无限递归)
它就是所谓的typeglob
,用于创建表别名。有关详细信息,请参阅perldoc 参考。