1

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

2 回答 2

4

以下将 package variable 别名$foo为 variable $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, of course, can just as easily be a lexical variable as a package variable. And since my $bar; actually returns the variable begin declared,

my $bar;
*foo = \$bar;

can be written as

*foo = \my $bar;

So,

my $caller = caller;
local(*{$caller."::a"}) = \my $a;
local(*{$caller."::b"}) = \my $b;

declares and aliases lexical variables $a and $b the similarly named package variables in the caller's namespace.

local simply causes everything to return to their original state once the sub is exited.

于 2012-12-10T07:03:13.963 回答
1

On scope

Perl has two variable name scoping mechnisms: global and lexical. Declaration of lexical vars is done with my, and they are accessibly by this name until they encounter a closing curly brace.

Global variables, on the other hand, are accessible from anywhere and do not have a scope. They can be declared with our and use vars, or do not have to be declared if strict is not in effect. However, they have namespaces, or packages. The namespace is a prefix seperated from the variable name by two colons (or a single quote, but never do that). Inside the package of the variable, the variable can be accessed with or without the prefix. Outside of the package, the prefix is required.

The local function is somewhat special and gives global variables a temporary value. The scope of this value is the same as that of a lexical variable plus the scopes of all subs called within this scope. The old value is restored once this scope is exited. This is called the dynamic scope.

On Globs

Perl organizes global variables in a big hash representing the namespace and all variable names (sometimes called the stash). In each slot of this hash, there is a so-called glob. A typeglob is a special hash that has a field for each of Perls native types, e.g. scalar, array, hash, IO, format, code etc. You assign to a slot by passing the glob a reference of a value you want to add - the glob figures out the right slot on it's own. This is also the reason you can have multiple variables with the same name (like $thing, @thing, %thing, thing()). Typeglobs have a special sigil, namely the asterisk *.

On no strict 'refs'

The no strict 'refs' is a cool thing if you know what you are doing. Normally you can only dereference normal references, e.g.

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.

The last line tried to dereference a string, this is considered bad practice. However, under no strict 'refs', we can access a variable of which we do not know the name at compile time, as we do here.

Conclusion

The caller functions returns the name of the package of the calling code, i.e. it looks up one call stack frame. The name is used here to construct the full names of $a and $b variables of the calling packages, so that they can be used there without a prefix. Then, these names are locally (i.e. in the dynamic scope) assigned to the reference of a newly declared, lexical variable.

The global variables $a and $b are predeclared in each package.

In the foreach loop, these lexicals are assigned different values (lexical vars take precedence over global vars), but the global variables $foo::a and $foo::$b point to the same data because of the reference, allowing the anonymous callback sub in the reduce call to read the two arguments easily. (See ikegamis answer for details on this.)

All of this hassle is good because (a) the effects are not externaly visible, and (b) the callback doesn't have to do tedious argument unpacking.

于 2012-12-10T07:16:55.823 回答