查询List :: Util :: reduce中的reg代码

时间:2012-12-10 06:02:25

标签: perl

我在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;
}

2 个答案:

答案 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完成词汇变量的声明,并且它们可以通过此名称访问,直到它们遇到结束大括号。

另一方面,全局变量可以从任何地方访问,并且没有范围。可以使用ouruse vars声明它们,或者如果strict无效,则不必声明它们。但是,他们有名称空间packages。命名空间是一个前缀,从变量名称中分隔两个冒号(或单引号,但从不这样做)。在变量的包中,可以使用或不使用前缀来访问变量。在包之外,需要前缀。

local函数有点特殊,它为全局变量赋予临时值。此值的范围与词法变量的范围相同,加上在此范围内调用的所有子范围。退出此范围后,将恢复旧值。这称为动态范围

On Globs

Perl在表示命名空间和所有变量名称(有时称为 stash )的大哈希中组织全局变量。在这个哈希的每个槽中,都有一个所谓的glob。 typeglob是一种特殊的散列,它具有每个Perls本机类型的字段,例如标量,数组,哈希,IO,格式,代码等。通过传递一个你想要添加的值的引用来分配给一个槽 - glob表示它自己的右边的插槽。这也是您可以拥有多个具有相同名称的变量的原因(例如$thing@thing%thingthing())。 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)回调不必做冗长的论证解包。