我在List :: Util中遇到了以下用于reduce子例程的代码。
my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;
我能理解reduce函数被称为:
my $sum = reduce { $a + $b } 1 .. 1000;
所以,我理解代码试图引用子程序中提到的$ a。但是,我无法正确理解意图。
作为参考,我正在添加子程序的完整代码
sub reduce (&@) {
my $code = shift;
require Scalar::Util;
my $type = Scalar::Util::reftype($code);
unless($type and $type eq 'CODE') {
require Carp;
Carp::croak("Not a subroutine reference");
}
no strict 'refs';
return shift unless @_ > 1;
use vars qw($a $b);
my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;
$a = shift;
foreach (@_) {
$b = $_;
$a = &{$code}();
}
$a;
}
答案 0 :(得分:4)
以下别名将变量$foo
打包到变量$bar
。
*foo = \$bar;
对一个的任何更改都会更改另一个,因为两个名称都引用相同的标量。
$ perl -E'
*foo = \$bar;
$bar=123; say $foo;
$foo=456; say $bar;
say \$foo == \$bar ? 1 : 0;
'
123
456
1
当然,您可以完全限定*foo
,因为它是符号表条目。以下别名包变量$main::foo
到$bar
。
*main::foo = \$bar;
或者,如果您在编译时不知道名称
my $caller = 'main';
*{$caller."::foo"} = \$bar; # Symbolic reference
当然, $bar
可以很容易地作为包变量的词法变量。由于my $bar;
实际上返回了声明的变量
my $bar;
*foo = \$bar;
可以写成
*foo = \my $bar;
所以,
my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;
在调用者名称空间中声明和别名词汇变量$a
和$b
类似命名的包变量。
local
只会在退出子状态后使一切恢复到原始状态。
答案 1 :(得分:1)
Perl有两个变量名称范围机制:全局和词汇。使用my
完成词汇变量的声明,并且它们可以通过此名称访问,直到它们遇到结束大括号。
另一方面,全局变量可以从任何地方访问,并且没有范围。可以使用our
和use vars
声明它们,或者如果strict
无效,则不必声明它们。但是,他们有名称空间或packages
。命名空间是一个前缀,从变量名称中分隔两个冒号(或单引号,但从不这样做)。在变量的包中,可以使用或不使用前缀来访问变量。在包之外,需要前缀。
local
函数有点特殊,它为全局变量赋予临时值。此值的范围与词法变量的范围相同,加上在此范围内调用的所有子范围。退出此范围后,将恢复旧值。这称为动态范围。
Perl在表示命名空间和所有变量名称(有时称为 stash )的大哈希中组织全局变量。在这个哈希的每个槽中,都有一个所谓的glob。 typeglob是一种特殊的散列,它具有每个Perls本机类型的字段,例如标量,数组,哈希,IO,格式,代码等。通过传递一个你想要添加的值的引用来分配给一个槽 - glob表示它自己的右边的插槽。这也是您可以拥有多个具有相同名称的变量的原因(例如$thing
,@thing
,%thing
,thing()
)。 Typeglobs有一个特殊的 sigil ,即星号*
。
no strict 'refs'
no strict 'refs'
如果你知道自己在做什么,那就太酷了。通常,您只能取消引用正常引用,例如
my @array = (1 .. 5);
my $arrayref = \@array; # is a reference
push @{$arrayref}, 6; # works
push @{array}, 6; # works; barewords are considered o.k.
push @{"array"}, 6; # dies horribly, if strict refs enabled.
最后一行尝试取消引用字符串,这被认为是不好的做法。但是,在no strict 'refs'
下,我们可以在编译时访问一个我们不知道名称的变量,就像我们在这里一样。
caller
函数返回调用代码包的名称,即它查找一个调用堆栈帧。此处使用该名称来构造调用包的$a
和$b
变量的全名,以便可以在没有前缀的情况下使用它们。然后,这些名称是local
ly(即在动态范围内)分配给新声明的词法变量的引用。
全局变量$a
和$b
在每个包中预先声明。
在foreach
循环中,这些词汇被赋予不同的值(词汇变量优先于全局变量),但全局变量$foo::a
和$foo::$b
指向相同的数据,因为引用,允许reduce
调用中的匿名回调子容易地读取这两个参数。 (有关详细信息,请参阅ikegamis的答案。)
所有这些麻烦都很好,因为(a)效果不是外部可见的,(b)回调不必做冗长的论证解包。