_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);
答案 0 :(得分:11)
理想情况下,该函数的作者本来希望使用
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
,其中包含对匿名子的引用。需要清除$_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 combinator。避免该问题的一种更简单的方法是将代码引用存储在包变量而不是词法变量中(因为只有子句捕获词法变量)。这就是您发布的代码所做的事情。请记住
*_fact = sub { ... };
基本上是
的运行时版本sub _fact { ... }
两者都将子分配给符号_fact
的CODE槽。
那就是说,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);
}
答案 1 :(得分:2)
上面的示例应该使用匿名子例程/闭包来编写:
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);
}
答案 2 :(得分:1)
通过为名为_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));
}
(记住,唯一比无限递归更糟糕的是......无限递归)
答案 3 :(得分:0)
它叫做typeglob
,用于创建表别名。有关详细信息,请参阅perldoc reference。