2

在 Perl 中,当使用sort带有自定义比较的函数时,变量$a$b已经分配给当前一对要比较的元素,例如:

@decreasing = sort { $b <=> $a } @list;

如何编写具有类似功能的其他子例程?例如,假设我想编写某种process_and_store函数,对列表的每个项目执行一些特殊操作,然后将其存储在数据库中;并且变量$item已经分配给正在处理的当前项目。例如,我想写一些类似的东西:

process_and_store { do_something_with($item); } @list;

而不是

process_and_store { my $item = shift; do_something_with($item); } @list;

我该怎么做呢?


更新:为了完整起见,虽然flesk 的答案没有问题,但为了“正确”本地化我对$item变量所做的更改,我必须遵循Axeman 的建议。在SomePackage.pm我放置了类似的东西:

package SomePackage;

use strict;

require Exporter;
our @ISA = qw/Exporter/;
our @EXPORT = qw(process_and_store);

our $item;

sub import { 
  my $import_caller = caller();
  {   no strict 'refs';
    *{ $import_caller . '::item' } = \*item;
  }
  # Now, cue Exporter!
  goto &{ Exporter->can( 'import' ) };
}

sub process_and_store (&@) {
  my $code = shift;
  for my $x (@_) {
    local *item = \$x;
    $code->();
    print "stored: $item\n"
  }
}

1;

然后我main.pl用类似这样的方式调用它:

#!/usr/bin/perl -w

use strict;
use SomePackage;

process_and_store { print "seen: $item\n"; } (1, 2, 3);

并得到预期的结果:

seen: 1
stored: 1
seen: 2
stored: 2
seen: 3
stored: 3
4

3 回答 3

4

在我的“关联数组”处理库中,我做了类似的事情。用户可以导出变量$k$v(键值),以便他们可以执行以下操作:

each_pair { "$k => $v" } some_source_list() 

这是我的做法:

  1. our ( $k, $v )在实现包中声明。
  2. import我允许包导出这些符号并在接收包中为它们起别名:*{$import_caller.'::'.$name} = \*{ $name };
  3. 在配对处理器中,我执行以下操作:

    local *::_ = \$_[0];
    local *k   = \$_[0];
    local *v   = \$_[1];
    @res = $block->( $_[0], $_[1] );
    

因此$k$v是队列中内容的别名。如果不必如此,那么您可能会对以下内容感到满意:

local ( $k, $v ) = splice( @_, 0, 2 );
local $_         = $k;

但可修改的副本也允许我执行以下操作:

each_pair { substr( $k, 0, 1 ) eq '-' and $v++ } %some_hash;

更新:

看来您忽略了第 2 步。您必须确保客户端包中的符号映射到您的符号。它可以很简单:

our $item;

sub import { 
    my $import_caller = caller();
    {   no strict 'refs';
        *{ $import_caller . '::item' } = \*item;
    }
    # Now, cue Exporter!
    goto &{ Exporter->can( 'import' ) };
}

然后,当您本地化自己的符号时,客户端包中的别名符号也会被本地化。

我可以看到它可以在没有local, 的情况下工作的主要方式是如果您从同一个包中调用它。否则,$SomePackage::item$ClientPackage::item是两个不同的东西。

于 2012-05-02T13:10:32.090 回答
2

我认为这有点像黑客,但你可以这样做:

#!/usr/bin/perl
use strict;
use warnings;

my $item;

sub process_and_store(&@) {
    my $code = shift;
    for (@_) {
        $item = $_;
        &$code();
    }
    undef $item;
}

问题是,$item必须是全局标量才能使其工作,因此process_and_store必须在循环列表时更新该标量。您还应该undef $item在子例程结束时限制任何潜在的副作用。如果我要写这样的东西,我会把它藏在一个模块中,并可以定义迭代器变量,以限制名称冲突。

测试:

my @list = qw(apples pears bananas);
process_and_store { do_something_with($item) } @list;

sub do_something_with {
    my $fruit = shift;
    print "$fruit\n";
}

输出:

apples
pears
bananas
于 2012-05-02T12:37:28.253 回答
2

$a$b变量在 Perl 中是特殊的;它们是真正的全局变量,因此不受use strict,并且也被sort()函数专门使用。

Perl 中的大多数其他类似用途将使用$_global 来处理这类事情:

process_and_store { do_something_with( $_ ) } @list;

这已经由正常$_规则处理。不要忘记localise $_

sub process_and_store(&@)
{
  my $code = shift;
  foreach my $item (@_) {
    local $_ = $item;
    $code->();
  }
}
于 2012-05-02T13:11:36.307 回答