Perl 5.18.2似乎接受“本地子例程”。
示例:
sub outer()
{
my $x = 'x'; # just to make a simple example
sub inner($)
{
print "${x}$_[0]\n";
}
inner('foo');
}
如果没有“本地子程序”,我会写:
#...
my $inner = sub ($) {
print "${x}$_[0]\n";
}
$inner->('foo');
#...
最重要的是,我认为两者是等效的。
然而,第一个变体并不像Perl抱怨的那样工作:
变量$x在以下位置不可用...
其中...
描述在“局部子例程”中引用$x
的行。
谁能解释这一点; Perl的本地子例程与Pascal的本地子例程有本质的不同吗?
3条答案
按热度按时间jq6vz3qz1#
问题中的术语“local subroutine”似乎指的是词法子例程。这些是私有子例程,只在定义它们的作用域(块)中可见,在定义之后;就像私有变量一样。
但它们是用
my
或state
定义(或预先声明)为my sub subname { ... }
的仅仅将
sub subname { ... }
写入另一个子例程中并不能使其成为“本地”(在任何版本的Perl中),但它的编译方式就像它是与另一个子例程一起编写的一样,并被放置在其包的符号表中(例如main::
)。在Perl中,闭包是程序中的一个结构,通常是一个标量变量,它引用了一个sub,并在创建时从其作用域携带环境变量。另请参见perlfaq 7中的一个条目。解释起来很混乱。例如:
匿名子函数“封闭”了定义它的作用域中的变量,也就是说,当它的生成函数返回它的引用并离开作用域时,由于引用的存在,这些变量仍然存在。由于匿名子函数是在运行时创建的,每次调用它的生成函数并重新创建其中的词法时,anon子函数也是如此。因此返回的对anon-sub的引用使用了词法数据,否则这些数据将消失。
回到“局部”子函数的问题上,如果我们想在这个问题中引入实际的闭包,我们需要从
outer
子例程返回一个代码引用,比如或者,根据主要问题,正如在v5.18.0中引入的以及从v5.26.0开始的稳定版本,我们可以使用命名词法(真正嵌套!)子例程
在这两种情况下,
my $f = outer(...);
都有从outer
返回的代码引用,它正确地使用了本地词法变量($x
)及其最新值。但是我们不能在
outer
中使用一个普通的名为sub的闭包这个
inner
是在编译时生成的,并且是全局的,所以它从outer
中使用的任何变量的值都是第一次调用outer
时的值。所以inner
只有在下一次调用outer
之前才是正确的--当outer
中的词法环境被重新生成而inner
没有被重新生成时。作为一个例子,我可以很容易地找到this post,并在perldiag中看到该条目(或将use diagnostics;
添加到程序中)。[2]在我看来,穷人的物品在某种程度上具有功能性和数据性,这些功能性和数据性是在另一个时间在别处制造的,并且可以与传递给它的数据一起使用(而且两者都可以更新)。
tktrz96b2#
如果需要“本地”子进程,则可以根据所需的向后兼容级别使用以下选项之一:
但是,您不应该使用
sub inner { ... }
。基本上与
所以
基本上是
正如您所看到的,
inner
甚至在outer
之外也是可见的,因此它根本不是“本地的”。正如您所看到的,对
*inner
的赋值是在编译时完成的,这引入了另一个主要问题。5.18确实引入了词法(“本地”)子例程。
如果需要支持旧版本的Perl,可以使用以下代码:
第一个
cygmwpex3#
我从
man perldiag
中找到了一个相当好的解释:因此,这是一个可能的解决方案:
......而这一个不会: