Perl排序; 干净地处理名称空间中的$ a,$ b包全局变量

use*_*909 11 sorting perl namespaces

假设我有一个other包含子程序(sort_it)的实用程序库(),我想用它来返回任意排序的数据.它可能比这更复杂,但这说明了关键概念:

#!/usr/local/bin/perl

use strict;

package other;

sub sort_it {
  my($data, $sort_function) = @_;

  return([sort $sort_function @$data]);
}
Run Code Online (Sandbox Code Playgroud)

现在让我们在另一个包中使用它.

package main;
use Data::Dumper;

my($data) = [
        {'animal' => 'bird',            'legs' => 2},
        {'animal' => 'black widow',     'legs' => 8},
        {'animal' => 'dog',             'legs' => 4},
        {'animal' => 'grasshopper',     'legs' => 6},
        {'animal' => 'human',           'legs' => 2},
        {'animal' => 'mosquito',        'legs' => 6},
        {'animal' => 'rhino',           'legs' => 4},
        {'animal' => 'tarantula',       'legs' => 8},
        {'animal' => 'tiger',           'legs' => 4},
        ],

my($sort_by_legs_then_name) = sub {
    return ($a->{'legs'}   <=> $b->{'legs'} ||
            $a->{'animal'} cmp $b->{'animal'});
};

print Dumper(other::sort_it($data, $sort_by_legs_then_name));
Run Code Online (Sandbox Code Playgroud)

由于一个微妙的问题,这不起作用. $a并且$b是包全局变量.他们指$main::a和$main::b当在封闭包裹起来.

我们可以通过说,而不是:

my($sort_by_legs_then_name) = sub {
    return ($other::a->{'legs'}   <=> $other::b->{'legs'} ||
            $other::a->{'animal'} cmp $other::b->{'animal'});
};
Run Code Online (Sandbox Code Playgroud)

这有效,但迫使我们在各处硬编码我们的实用程序包的名称.如果要改变,我们需要记住改变代码,而不仅仅是use other qw(sort_it);可能存在于现实世界中的陈述.

您可能会立即考虑尝试使用__PACKAGE__.最终评估为"主要".那样做eval("__PACKAGE__");.

使用caller它有一个技巧:

my($sort_by_legs_then_name) = sub {
  my($context) = [caller(0)]->[0];
  my($a) = eval("\$$context" . "::a");
  my($b) = eval("\$$context" . "::b");

  return ($a->{'legs'}   <=> $b->{'legs'} ||
          $a->{'animal'} cmp $b->{'animal'});
};
Run Code Online (Sandbox Code Playgroud)

但这是相当黑魔法的.似乎应该有更好的解决方案.但我还没有找到它或者想出来.

DVK*_*DVK 9

使用原型(最初由ysth在Usenet发布中提出的解决方案).

适用于Perl> = 5.10.1(之前不确定).

my($sort_by_legs_then_name) = sub ($$) {
    my ($a1,$b1) = @_;
    return ( $a1->{'legs'} <=> $b1->{'legs'} ||
            $a1->{'animal'} cmp $b1->{'animal'});
};
Run Code Online (Sandbox Code Playgroud)

我得到的结果是:

$VAR1 = [
      {
        'legs' => 2,
        'animal' => 'bird'
      },
      {
        'legs' => 2,
        'animal' => 'human'
      },
      {
        'legs' => 4,
        'animal' => 'dog'
      },
      {
        'legs' => 4,
        'animal' => 'rhino'
      },
      {
        'legs' => 4,
        'animal' => 'tiger'
      },
      {
        'legs' => 6,
        'animal' => 'grasshopper'
      },
      {
        'legs' => 6,
        'animal' => 'mosquito'
      },
      {
        'legs' => 8,
        'animal' => 'black widow'
      },
      {
        'legs' => 8,
        'animal' => 'tarantula'
      }
    ];
Run Code Online (Sandbox Code Playgroud)

  • 更改在[Perl 5.6](http://search.cpan.org/~gsar/perl-5.6.0/pod/perldelta.pod#Enhanced_support_for_sort%28%29_subroutines)中进行.尽管如此,还是有[记录在案的性能惩罚](http://perldoc.perl.org/functions/sort.html). (4认同)
  • 与使用匿名子例程相比,性能损失并不是那么糟糕,但两者都比使用块慢得多:http://gist.github.com/603932这是一个抽象可能不是你的朋友的senario. (3认同)