2009-08-24 16 views
7

私はPerlクラスを持っています。それはsort()方法を持っている、と私はそれがsort()機能内蔵に多かれ少なかれ同じになりたい:Perl可変範囲の質問

$object->sort(sub ($$) { $_[0] <=> $_[1] }); 

しかし、私は行うことはできません。そのため、スコープの

$object->sort(sub { $a <=> $b }); 

。しかし、List :: Utilモジュールはreduce()でこれを行います。私はList :: Utilモジュールを見て、これを実現させるために、no strict 'vars'でかなり厄介なことをします。私はそれを試みたが、役に立たなかった。

reduce()は、適切な名前空間にエクスポートされているため、私のクラスはこれを行うことができません。私の理解では、関数が別の名前空間にしっかりと入っています。これは正しいのですか、それとも私の状況でこれを行うためのいくつかの(間違いなく恐ろしい、あまりアドバイスされていない)方法がありますか?

答えて

8

まあ、他の2つの答えは、右半分の両方です。

package Foo; 

use strict; 
use warnings; 

sub sort { 
    my ($self, $sub) = @_; 

    my ($pkg) = caller; 
    my @x = qw(1 6 39 2 5); 
    print "@x\n"; 
    { 
     no strict 'refs'; 
     @x = sort { 
      local (${$pkg.'::a'}, ${$pkg.'::b'}) = ($a, $b); 
      $sub->(); 
     } @x; 
    } 
    print "@x\n"; 

    return; 
} 


package main; 

use strict; 
use warnings; 

my $foo = {}; 
bless $foo, 'Foo'; 

$foo->sort(sub { $a <=> $b }); 
# 1 6 39 2 5 
# 1 2 5 6 39 

実際にオブジェクトの一部であるデータをソートするとします。

$a$bを、Perlが探している発信者のパッケージにローカライズするには、callerという魔法が必要です。そのサブが呼び出されている間だけ存在するグローバル変数を作成しています。

warningsで「一度だけ使用された名前」が表示されます。私は、何とかこれを避けるために飛び越えることができるいくつかのフープがあると確信しています。

+2

あなたの目的には十分に良いかもしれませんが、壊れやすいです。比較関数が 'sort'メソッドの呼び出し元と同じパッケージに属するという保証はありません。それはSub :: Identifyが入るところです。 – cjm

+0

@cjm - これは事実です。私は間違いなくSub :: Identifyを見ていきますが、私の大きな問題は一般的なケースでは機能しないようにすることです。特定のソリューションは一般的な障害よりも優れています。しかし、この答えをあなたのものと組み合わせると、私には良い解決策である一般的な解決策が与えられます。 –

+1

'sort'組み込み関数も同じ問題を抱えています。比較関数は呼び出し側と同じパッケージから来ると仮定します。だからあなたがそれで生きることができるならば、Sub :: Identifyへの依存関係を保存します。 (条件付きでSub :: Identifyを要求し、それがインストールされていなければ 'caller'にフォールバックすることもできますが、それ以上の作業です) – cjm

1

あなたは、サブルーチンコールの期間$a$bの値を設定するthe local operatorを使用することができます。

sub sample 
{ 
    my $callback = shift; 
    for (my $i = 0; $i < @_; $i += 2) { 
     local ($a, $b) = @_[$i, $i + 1]; 
     $callback->(); 
    } 
}  

sample sub { print "$a $b\n" }, qw(a b c d e f g h i j); 

あなたは普通のサブルーチンではなく、メソッドを持っている場合、あなたはそれがさらに可能にすることができsortのように、コールバック関数の前にsubを使用する必要はありません。機能上のプロトタイプを使用します。

sub sample (&@) 

その後、あなたはこのようにそれを呼び出す呼び出す:

sample { print "$a $b\n" } qw(a b c d e f g h i j); 

方法は、しかし、プロトタイプの影響を受けていません。

+0

彼は具体的に 'メソッド'について質問しています。 –

+3

クラスの外からメソッドを呼び出すと動作しません。あなたは*クラスの* $ aと$ bをローカライズしています。 – cjm

+0

ああ。私はこれが終わったのを見たと思ったが、私はそうは思わない。 perlvarからの私の印象は、 '$ a'と' $ b'は、この状況で「ちょうどうまくいく」という魔法であるということでした。 –

3

Sub::Identifyを使用して、コードレファに関連付けられたパッケージ(stash_name)を見つけることができます。次に、必要に応じてそのパッケージに$ aと$ bを設定します。あなたの方法でそれを動作させるためにno strict 'refs'を使用する必要があるかもしれません。

はここで一般的なケースで動作するように修正eveeさんの答えです:

use strict; 
use warnings; 

package Foo; 

use Sub::Identify 'stash_name'; 

sub sort { 
    my ($self, $sub) = @_; 

    my $pkg = stash_name($sub); 
    my @x = qw(1 6 39 2 5); 
    print "@x\n"; 
    { 
     no strict 'refs'; 
     @x = sort { 
      local (${$pkg.'::a'}, ${$pkg.'::b'}) = ($a, $b); 
      $sub->(); 
     } @x; 
    } 
    print "@x\n"; 

    return; 
} 


package Sorter; 

sub compare { $a <=> $b } 

package main; 

use strict; 
use warnings; 

my $foo = {}; 
bless $foo, 'Foo'; 

$foo->sort(\&Sorter::compare); 

$foo->sort(sub { $b <=> $a }); 
+1

List :: Utilは単に 'caller'を使います。私はそれが十分ではないと思います、一般的なケースでは、それは?呼び出し元が他のパッケージから関数を渡すと、List :: Utilは関数の代わりに呼び出し元の '$ a'を設定します。 –

+0

5.10.1のList :: Utilのバージョンは、基本的にSub :: Identifyがcoderefが属するパッケージを把握するのと同じことをするXSコードを使用します。 – cjm

+0

また、私はモジュールをXSで書き直すことができたので、XSを学ぶ機会を与える依存関係を失いました。しかし、その間、私はこれを調べます。 –