这里的“proper closure”是什么意思?

6

这是直接从Perl Cookbook摘录的代码:

@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
no strict 'refs';
*$name = sub { "<FONT COLOR='$name'>@_</FONT>" };
}

它的意图是形成6个不同颜色名称的子例程。在解释部分,书中写道:
这些函数看起来都是独立的,但实际上只编译了一次真正的代码。这种技术节省了编译时间和内存使用。为了创建一个适当的闭包,匿名子例程中的任何变量都必须是词法变量。这就是循环迭代变量上的my的原因。 proper closure指的是正确的闭包,如果省略my,则会出现错误。另外,即使类型全局变量不能为词法变量定义并应该抛出错误,但为什么类型全局变量能与词法变量一起工作?
3个回答

7
正如其他人所提到的,这本食谱书使用术语“proper”来指代一个子程序被创建,其中包含一个来自更高词法范围的变量,而且这个变量不能再通过任何其他方式访问。我使用了过于简单的助记符“无法访问$color变量”来记忆闭包的这一部分。
声明“类型全局变量无法定义为词法变量”误解了一些关键点。如果你将其理解为“无法使用'my'来创建类型全局变量”,那么这种说法在某种程度上是正确的。考虑以下内容:
my *red = sub { 'this is red' };

这段代码会因为“syntax error near "my *red"”而报错,因为它试图使用“my”关键字定义一个typeglob。

然而,你的示例代码并不是在尝试这样做。它正在定义一个typeglob,该typeglob是全局的,除非被覆盖。它使用词法变量的值来定义typeglob的名称。

顺便说一下,typeglob可以是词法局部的。考虑以下代码:

my $color = 'red';

# create sub with the name "main::$color". Specifically "main:red"
*$color = sub { $color };

# preserve the sub we just created by storing a hard reference to it.
my $global_sub = \&$color;

{
  # create a lexically local sub with the name "main::$color".
  # this overrides "main::red" until this block ends
  local *$color = sub { "local $color" };

  # use our local version via a symbolic reference.
  # perl uses the value of the variable to find a
  # subroutine by name ("red") and executes it
  print &$color(), "\n";

  # use the global version in this scope via hard reference.
  # perl executes the value of the variable which is a CODE
  # reference.
  print &$global_sub(), "\n";

  # at the end of this block "main::red" goes back to being what
  # it was before we overrode it.
}

# use the global version by symbolic reference
print &$color(), "\n";

这是合法的,输出将会是:
local red
red
red

在警告下,它会抱怨“子例程main :: red被重新定义”。

谢谢。你能解释一下为什么块内的第二个 print &$global_sub() 输出 red,即使 $global_sub 持有一个名为 redsub 的引用,并且这个 sub 刚刚在块内被重新定义了吗? - SexyBeast
1
@Cupidvogel $global_sub 包含一个 CODE 引用。这是对编译代码的引用,而不是对子例程名称的引用。它是通过引用子程序名称创建的,但一旦存储了引用,该引用就不再由该名称定义。使用它的行不通过名称调用它,而是使用对代码本身的引用来调用它。我添加了符号(按名称)和硬(CODE)引用被使用时的澄清。希望这有所帮助。 - benrifkah

6
我认为“正确的闭包”就是真正的闭包。如果$name不是词法变量,所有的子程序都会引用同一个变量(其值将被重置为循环之前的任何值)。 *$name使用$name的值作为*符号解除引用的参考。由于$name是一个字符串,它是符号引用(因此没有严格的“refs”)。

1

来自perlfaq7

什么是闭包?

闭包在perlref中有详细的说明。

闭包是一个计算机科学术语,具有精确但难以解释的含义。通常,在Perl中,闭包被实现为带有对其外部作用域中词法变量的持久引用的匿名子例程。这些词法变量神奇地指向在定义子例程时存在的变量(深度绑定)。

闭包最常用于编程语言中,其中函数的返回值本身可以是一个函数,就像在Perl中一样。请注意,某些语言提供匿名函数,但无法提供正确的闭包:例如Python语言。有关闭包的更多信息,请查阅任何有关函数式编程的教材。Scheme是一种不仅支持而且鼓励使用闭包的语言。

答案继续详细回答了这个问题。

Perl FAQs 是一个很好的资源。但是如果没有人阅读它们,维护它们就有点浪费精力。

编辑(为了更好地回答您后面的问题):

1/ 什么是适当的闭包?-请参见上文

2/ 如果省略 my 会发生什么?-它不会起作用。试一试,看看会发生什么。

3/ 为什么类型全局变量可以与词法变量一起使用?-变量 $name 只用于定义要使用的类型全局变量的名称。它实际上并没有被用作类型全局变量。

这样更有帮助吗?


那非常有帮助,但并不是我正在寻找的答案。 - SexyBeast

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