Perl闭包和$_

16

在一个不熟悉的编程语言中,我尝试学习的第一件事是它如何处理闭包。它们的语义常常与语言处理作用域和其他各种棘手的部分相互交织,因此理解它们可以揭示语言的其他几个方面。此外,闭包是一种非常强大的结构,通常可以减少我需要输入的模板代码量。所以我正在尝试使用Perl闭包时,偶然发现了一个小问题:

my @closures;
foreach (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}

当我编写上面的代码时,我原本期望看到

I will remember 1
I will remember 2
I will remember 3

但是我得到的是I will remember CODE(0x986c1f0)

上述实验揭示了$_非常依赖上下文环境,如果它出现在闭包中,则其值在创建闭包的时候并没有被固定。它更像是一个引用。在perl创建闭包时还应该注意哪些问题?

2个回答

24
闭包只会关闭词法变量;$_ 通常是全局变量。 在5.10及以上版本中,可以使用 my $_; 将其限定为特定作用域内的词法变量(虽然在5.18中,这被追溯地宣布为实验性内容并可能会更改,因此最好使用其他变量名)。
这将产生您期望的输出:
use strict;
use warnings;
use 5.010;
my @closures;
foreach my $_ (1..3) {
  # create some closures
  push @closures, sub { say "I will remember $_"; };
}
foreach (@closures) {
  # call the closures to see what they remember
  # the result is not obvious
  &{$_}();
}

2
但这与使用 foreach my $num ... 几乎相同。 - David K.
1
那么,如果它们只关闭词法变量,其他类型的变量会发生什么?闭包是否像对于 $_ 一样使用调用站点可用的值? - David K.
1
@davidk01:就像其他允许全局变量的编程语言一样,闭包实际上像普通变量一样封闭了该变量。你只是忘记了它是全局的,因此当其他代码将其值设置为其他值时(例如通过匿名子程序本身在调用时),它可以更改。 - slebetman
这是一种相当复杂的编写 &$_() for @closuresfor (@closures) { $_->() } 的方式。:) 另一方面,既然没有参数,那么在这里只使用普通的 &$_ 就足够了。 - tchrist
@tchrist:我故意尽可能保留了问题中的原始版本。 - ysth
显示剩余3条评论

4

$_ 是一个全局变量,不应在闭包中使用。在使用之前,请将其分配给词法作用域变量,如下所示。这将产生预期的输出。

代码示例:

my $var = $_;
sub {
  # use $var instead of $_ here
}

#!/usr/bin/perl -w

use strict;
my @closures;


foreach (1..3) {
   my $var = $_;
   push @closures, sub { print "I will remember $var"; };
}

foreach (@closures) {
  $_->();
  print "\n";
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接