3

次のperlコードは、PerlCriticで(Activestateによる)警告を生成します。

sub natural_sort {
    my @sorted;
    @sorted = grep {s/(^|\D)0+(\d)/$1$2/g,1} sort grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
}

生成される警告は次のとおりです。

リスト関数の$_を変更しないでください

その警告についての詳細はこちら

$ _を変更しているとは思わないので、警告がわかりません。ただし、変更する必要があると思います。誰かが私にそれを説明してもらえますか?

4

5 に答える 5

10

を使用しているため、両方grepが変更されています。たとえば、これは次のとおりです。$_s//

grep {s/(^|\D)0+(\d)/$1$2/g,1}

これと同じです:

grep { $_ =~ s/(^|\D)0+(\d)/$1$2/g; 1 }

sでmap何もフィルタリングしていないので、イテレータとして使用しているだけなので、使用したほうがよいと思います。grepgrep

sub natural_sort {
    my $t;
    return map { ($t = $_) =~ s/(^|\D)0+(\d)/$1$2/g; $t }
           sort
           map { ($t = $_) =~ s/(\d+)/sprintf"%06.6d",$1/ge; $t }
           @_;
}

それは同じことをし、批評家を静かに保つべきです。List::MoreUtilsプレーンよりも優れたリスト演算子が必要な場合は、確認することをお勧めしますmap

于 2011-05-05T08:07:18.443 に答える
3

grepで置換(つまりs///)を実行しています。これにより、grep$_さ​​れているリストが変更されます。

于 2011-05-05T07:53:13.683 に答える
3

これと他のケースはperldocperlvarで説明されています:

使用しなくてもPerlが$_を想定する場所は次のとおりです。

  • 次の機能:

abs、alarm、chomp、chop、chr、chroot、cos、defined、eval、exp、glob、hex、int、lc、lcfirst、length、log、lstat、mkdir、oct、ord、pos、print、quotemeta、readlink、 readpipe、ref、require、reverse(スカラーコンテキストのみ)、rmdir、sin、split(2番目の引数)、sqrt、stat、study、uc、ucfirst、unlink、unpack。

  • デフォルトがSTDINである-tを除くすべてのファイルテスト(-f、-d)。-Xを参照してください

  • =〜演算子なしで使用した場合のパターンマッチング操作m //、s///およびtr///(別名y ///)。

  • 他の変数が指定されていない場合のforeachループのデフォルトのイテレーター変数。

  • grep()およびmap()関数の暗黙のイテレータ変数。

  • give()の暗黙の変数。


  • whileテストの唯一の基準として、操作の結果が単独でテストされるときに入力レコードを配置するデフォルトの場所。しばらくのテスト以外では
    、これは起こりません。

于 2011-05-05T09:07:08.740 に答える
2

多くの人がsオペレーターが変更していると正しく答えていますが$_、まもなくリリースされるPerl 5.14.0には、オペレーター(つまり)の新しいrフラグがあり、インプレースで変更するのではなく、変更された要素を返します。詳細については、TheEffectivePerlerをご覧ください。perlbrewを使用して、この新しいバージョンをインストールできます。ss///r

編集:Perl 5.14が利用可能になりました!アナウンス アナウンス デルタ

これは、mu(を使用map)によって提案された関数ですが、この機能を使用しています。

use 5.14.0;

sub natural_sort {
    return map { s/(^|\D)0+(\d)/$1$2/gr }
           sort
           map { s/(\d+)/sprintf"%06.6d",$1/gre }
           @_;
}
于 2011-05-05T14:37:59.343 に答える
1

他の答えが見逃している非常に重要な部分は、その行が

grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;

関数に渡された引数を実際に変更しているのであって、それらのコピーではありません。

grepはフィルタリングコマンドであり$_、コードブロック内の値はの値の1つのエイリアスです@_@_次に、関数に渡された引数のエイリアスが含まれているため、s///演算子がその置換を実行すると、元の引数に変更が加えられます。これを次の例に示します。

sub test {grep {s/a/b/g; 1} @_}

my @array = qw(cat bat sat);

my @new = test @array;

say "@new";   # prints "cbt bbt sbt" as it should
say "@array"; # prints "cbt bbt sbt" as well, which is probably an error

探している動作(リストのコピーに変更する関数を適用する)は、関数としていくつかのモジュールに$_カプセル化されています。apply私のモジュールList::Genには、そのような実装が含まれています。 apply自分で書くのもかなり簡単です。

sub apply (&@) {
    my ($sub, @ret) = @_;
    $sub->() for @ret;
    wantarray ? @ret : pop @ret
}

これで、コードは次のように書き直すことができます。

sub natural_sort {
    apply {s/(^|\D)0+(\d)/$1$2/g} sort apply {s/(\d+)/sprintf"%06.6d",$1/ge} @_
}

繰り返し置換を行う目的が、一時的な変更を適用して元のデータの一種を実行することである場合は、その目標を達成するためのより効率的な方法であるシュワルツ変換と呼ばれるPerlイディオムを調べる必要があります。

于 2011-05-05T14:25:37.697 に答える