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(key-value)をエクスポートして、次のようなことができるようにすることができます。

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::item2$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また、潜在的な副作用を制限するために、サブルーチンの最後に行う必要があります。もし私がこのようなものを書くとしたら、それをモジュールに隠して、名前の競合を制限するために iterator 変数を定義できるようにします。

テスト:

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

$aand$b変数は Perl では特別です。それらは実際のグローバル変数であるため、 から除外さuse strictれ、sort()関数によっても特別に使用されます。

Perl での他の同様の用途のほとんどは$_、この種のものにグローバルを使用します:

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 に答える