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中对阶乘使用递归是非常浪费的。