簡體   English   中英

神秘的*在嵌套子的前面

[英]mysterious * in front of nested 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 $_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組合器 避免該問題的一種更簡單的方法是將代碼引用存儲在包變量而不是詞法變量中(因為只有子句捕獲詞法變量)。 這就是您發布的代碼所做的事情。 請記住

*_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);
}

檢查typeglob別名

應使用匿名子例程/閉包編寫上面的示例:

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);
}

通過為名為_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));
}

(記住,唯一比無限遞歸更糟的是......無限遞歸)

它叫做typeglob ,用於創建表別名。 有關詳細信息,請參閱perldoc參考

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM