perl:在呼叫者上下文中定义变量



我创建了这个简单的子例程。

use List::Util qw(pairmap);
sub pairGroupBy(&@) {
  my($irCode, @iaItems) = @_;
  my %laResult = ();
  pairmap {
    my $lsKey = $irCode->();
    if (!defined($lsKey)) {
      die "Trying to pairGroup by nonexisting key '$lsKey'";
    }
    push @{$laResult{$lsKey}}, $a => $b;
  } @iaItems;
  return %laResult;
}

它运行良好,直到从定义的同一文件中使用子例程为止。当我将其移动到某些软件包时,变量$a$b$irCode->()回调中不确定。

我已经从列表中学到了:: util源代码,此代码可以解决问题:

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

所以我以这种方式修改了我的子例程:

use List::Util qw(pairmap);
sub pairGroupBy(&@) {
  my($irCode, @iaItems) = @_;
  my $caller = caller;
  my %laResult = ();
  pairmap {
    no strict 'refs';
    local(*{$caller."::a"}) = $a; # <---- the line 96
    local(*{$caller."::b"}) = $b;
    my $lsKey = $irCode->();
    if (!defined($lsKey)) {
      die "Trying to pairGroup by nonexisting key '$lsKey'";
    }
    push @{$laResult{$lsKey}}, $a => $b;
  } @iaItems;
  return %laResult;
}

,但我需要使用no strict 'refs';行(列表:: UTIL源代码不使用它)。否则出现错误消息:

Can't use string ("main::a") as a symbol ref while "strict refs" in use at /home/.../bin/SatFunc.pm line 96.

我的问题是:有没有更好的方法在不使用no strict 'refs';的情况下定义$a$b变量?

我希望我的功能以与paimmap,pairgrep等相同的方式使用

编辑:@simbabque询问了一个示例,如何使用该函数。这是一个例子:

my %laHoH = (
  aa => {
    color => 'yellow',
    item => 'sun',
    active => 1
  },
  bb => {
    color => 'blue',
    item => 'sky',
    active => 1
  },
  cc => {
    color => 'green',
    item => 'grass',
    active => 0
  },
  dd => {
    color => 'blue',
    item => 'watter',
    active => 1
  }
);

my %laGrouped = pairGroupBy {
  $b->{color}
} pairgrep {
  $b->{active}
} %laHoH;

该功能然后返回此结构:

{
  'yellow' => [
                'aa',
                {
                  'color' => 'yellow',
                  'item' => 'sun',
                  'active' => 1
                }
              ],
  'blue' => [
              'dd',
              {
                'active' => 1,
                'item' => 'watter',
                'color' => 'blue'
              },
              'bb',
              {
                'color' => 'blue',
                'item' => 'sky',
                'active' => 1
              }
            ]
};

我不确定您为什么看到这个问题,但我怀疑您的想法过于思考。在这样的空白中使用pairmap似乎是一个坏主意。

您不能只是将数组转换为哈希,然后在其中迭代?

my %iaItemsHash = @iaItams;
while (my ($k, $v) = each %iaItemsHash) {
  my $lsKey = $irCode->();
  if (!defined($lsKey)) {
    die "Trying to pairGroup by nonexisting key '$lsKey'";
  }
  push @{$laResult{$lsKey}}, $k => $v;
}

更新:根据您的评论,我已经重新阅读了您的原始问题,并发现您正在谈论使用$irCode->()调用访问变量。

我的解决方案的问题是$k$v是词汇变量,因此,在其词汇范围之外不可用(通常被视为功能!),解决方案是诉诸良好的编程实践和良好的编程实践和将值作为参数发送到子例程中。

是否有一些更好的方法在不使用no strict 'refs';?

的情况下定义$a$b变量

您在问我们如何执行符号派,同时要求Perl阻止您摆脱象征性的延期。没有理由这样做。如果您想执行象征性的表现,请不要要求Perl阻止您这样做。

即使perl没有抓住您的操作(即,如果您设法找到一种不触发use strict qw( refs );的方法),您仍然会使用符号消失!您只是向自己和读者撒谎。

相反,最好记录您在做什么。使用no strict qw( refs );来表示您正在使用use strict qw( refs );可以阻止。


以下与您的代码相同结构的方法的浪费要少得多:

my %laGrouped;
for my $key (keys(%laHoH)) {
   my $rec = $laHoH{$key};
   next if !$rec->{active};
   push @{ $laGrouped{ $rec->{color} } }, $key, $rec;
}

,但也可以改善结构。以下方法产生的结构易于使用:

my %laGrouped;
for my $key (keys(%laHoH)) {
   my $rec = $laHoH{$key};
   next if !$rec->{active};
   $laGrouped{ $rec->{color} }{$key} = $rec;
}

如果您发现自己正在使用pairGroupBy,则可能在某个地方出错了。但是,这是出于教育目的更好的实施:

sub pairGroupBy(&@) {
   my $cb = shift;
   my $caller = caller;
   my $ap = do { no strict 'refs'; *{ $caller.'::a' } };  local *$ap;
   my $bp = do { no strict 'refs'; *{ $caller.'::b' } };  local *$bp;
   my %groups;
   while (@_) {
      *$ap = shift;
      *$bp = shift;
      my $group = $cb->();
      push @{ $groups{$group} }, $a, $b;
   }
   return %groups;
}

最新更新