7

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;
       ...
   }

ただし、sort関数はより複雑であるため、上記のコードにうまく適合しないと仮定します。

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

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

4

3 に答える 3

9

なんらかの理由で問題のあるコードを表示しなかったのですが、

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

   my @newArr = sort sort_func @oldArr;
}
私はこのメカニズムがどのように機能するかを理解しようとしています。[...]これは、subが1回解析され、forループのevry反復では再作成されないためだと思います。

完全ではありません。以下は、サブを1回だけ解析およびコンパイルしますが、機能します。

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

   my @newArr = sort $cmp_func @oldArr;
}

重要なのは、何$valがキャプチャされるかです。評価$val時にキャプチャされます。sub { ... }そのことを念頭に置いて

sub foo { ... }

この点で以下と同じです、

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

私のコードでは$val、foreachループからをキャプチャします。あなたの場合、コンパイル時にキャプチャするので、コンパイル時に$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;
}

または、必要な値をキャプチャする代わりに、比較関数に渡すこともできます。

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;
}
于 2012-05-08T15:57:00.393 に答える
8

とに加えて引数を取る関数を使用したいとし$aます$b

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のメカニズムsortはやや特殊であり、純粋なPerlでは簡単に複製できません。

于 2012-05-08T15:29:29.850 に答える
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;
}

これは、実際には、より効率的で、読みやすく、価値があるわけではありませんが、実際に役立つ場所でのテクニックについて知ることは興味深いかもしれません。

于 2012-05-08T15:38:35.893 に答える