2012-05-08 11 views
7

私はperlのコードブロックに関する質問があります。次のコードを与えます。perlコードブロック

my @newArr = sort { $a <=> $b } @oldArr; 

は、引数としてコードブロックを使用します。

ように私はそれを書き換えることができます:私は、このメカニズムがどのように動作するかを把握しようとしています

sub sortFunc { 
     return $a <=> $b; 
    } 
    my @newArr = sort sortFunc @oldArr; 

。 現在、私はコードブロック内で乱雑に見えるような複雑なソート関数を実装する必要がありますが、いくつかのローカル変数に依存します。たとえば :

foreach my $val (@values){ 
     my @newArr = sort { $hash{$a}{$val}<=> $hash{$b}{$val} } @oldArr; 
     ... 
    } 

はなく、ソート機能がより複雑であることを前提としていますので、きちんと上記のコードに適合文句を言いません。

(forループのスコープ内でローカルに定義されている)関数を使用しようとすると、「ハッシュ要素で初期化されていない値を使用する」が得られます。

これは、サブが一度解析され、forループのevry反復のために再作成されないためです。私はコードブロックを実装する方法を理解しようとしています。反復ごとに再解析されるか、パラメータを渡す方法があります。

+0

'私の@newArr =ソート{$ <=> $ B} @oldArr;' @oldArrは、これに応じて何を意味するのでしょうか? –

+0

"しかし、ソート関数がより複雑であると仮定することができます" - >たぶんこのソート関数のコードを投稿するべきです。 – TLP

答えて

9

あなたはいくつかの理由のために問題のあるコードを示さなかったが、私はそれが私がこのメカニズムがどのように動作するかを把握しようとしています

for my $val (@values) { 
    sub sort_func { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    } 

    my @newArr = sort sort_func @oldArr; 
} 
のようなものだと思います。 [...]それは、サブが一度解析され、forループのevry反復のために再作成されないためです。

以下だけパース、一度サブをコンパイルし、まだそれが動作:何$valがキャプチャされたときに

for my $val (@values) { 
    my $cmp_func = sub { 
     return $hash{$a}{$val} <=> $hash{$b}{$val}; 
    }; 

    my @newArr = sort $cmp_func @oldArr; 
} 

は大事なことです。 sub { ... }を評価すると、$valがキャプチャされます。私のコードでは、この点で、以下のよう

sub foo { ... } 

が同じであることを念頭に置いて

BEGIN { *foo = sub { ... }; } 

を維持する、それがforeachループから$valをキャプチャします。あなたの中では、コンパイル時にキャプチャするので、コンパイル時に存在していた$valをキャプチャします。それはあなたが望む変数ではありません。

これで問題なく動作するようになったので、複雑なコードを必要に応じてループの外に移動することができます。

sub make_cmp_func { 
    my ($hash, $val) = @_; 
    return sub { 
     return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
    }; 
} 

for my $val (@values) { 
    my $cmp_func = make_cmp_func(\%hash, $val); 
    my @newArr = sort $cmp_func @oldArr; 
} 

また、必要な値を取得する代わりに、compare関数に渡すこともできます。

sub cmp_func { 
    my ($hash, $val, $a, $b) = @_; 
    return $hash->{$a}{$val} <=> $hash{$b}{$val}; 
} 

for my $val (@values) { 
    my @newArr = sort { cmp_func(\%hash, $val, $a, $b) } @oldArr; 
} 
+0

長さ、しかし、あなたはソリューションのためだけでなく、それがどのように機能するのかを知りたがっています。 – ikegami

+0

ありがとう、 私は関数の代わりにコード参照を使用することを検討していましたが、それが何か別のことをするかどうかはわかりませんでした。 – Smartelf

+0

あなたの最後の例では、 '$ a'と' $ b'を渡して再宣言する必要がありますか? – TLP

8

$a$bに加えて引数を取る関数を使いたいとします。 sortでコードブロックを使用するため

sub my_sort_func { 
    my ($val, $a, $b) = @_; 
    return $hash{$a}{$val} <=> $hash{$b}{$val}; 
} 

foreach my $val (@values) { 
    my @newArr = sort { my_sort_func($val,$a,$b) } @oldArr; 
    ... 
} 

Perlの機構は、やや特殊であり、容易に純粋なPerlで複製されません。

5

これは「巧妙だが必ずしもスマートではない」多様性の1つである。余分なパラメータに拒否すれば、代わりにカリングを使うことができます。

sub make_sorter { 
    my ($hashref, $val) = @_; 
    return sub { 
      $hashref->{$a}{$val} <=> $hashref->{$b}{$val} 
    }; 
} 

for my $val (@values) { 
    my $sorter = make_sorter(\%hash, $val); 
    my @newArr = sort $sorter @oldArr; 
} 

これは本当にどのような方法で、より効率的に、より読みやすい、またはそれ以上の価値がありませんが、それは実際に便利だどこかにするための技術について知っていることは興味深いかもしれません。

+0

私は 'sort {make_sorter($ hash、$ val) - >($ a、$ b)} @ oldArr'を得たときにこのパスを開始して答えを変更しました... – mob

関連する問題