2011-05-05 91 views
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} @_; 
} 

產生的警告是:

不要修改$ _在列表功能

More info about that warning here

我不明白的警告,因爲我不認爲我修改$ _做,但我想 我必須。 有人可以向我解釋嗎?

回答

10

由於您使用的是s//,因此兩個grep都在修改$_。例如,這樣的:

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

是一樣的:

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

我想你會使用map你不是你grep中濾波什麼都好了,你只是使用grep作爲迭代器:

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 } 
      @_; 
} 

這應該做同樣的事情並保持批評者的安靜。如果您想要比普通map更好的列表運算符,您可能需要查看List::MoreUtils

+0

測試和工程有關這個主題的相同 – Craig 2011-05-05 09:27:30

3

您正在grep中進行替換(即s///),它修改了$_,即列表被grepped。

3

這和其他案件perldoc perlvar解釋:

這裏集中的地方Perl將 承擔$ _即使你不使用它:

  • 以下功能:

ABS,報警,格格,印章,CHR,chroot環境, COS,定義,EVAL,EXP,水珠,六角, INT,LC,LCF開始步驟,長度,日誌,LSTAT, MKDIR,辛,ORD,POS,打印, quotemeta,的readlink,readpipe,REF 需要,反向(在標量上下文 只),命令rmdir,罪,分割(在其 第二參數),sqrt,stat,study, uc,ucfirst,unlink,unpack。

  • 所有文件測試(-f,-d)除了-t,默認爲STDIN。 請參閱-X

  • 使用 而沒有= =運算符時,模式匹配操作m //,s ///和tr ///(又名y ///)。

  • 如果沒有提供其他變量,則在foreach循環中默認的迭代器變量爲 。

  • grep()和map()函數中的隱式迭代器變量。

  • given()的隱式變量。

  • 默認的地方,把輸入記錄時,操作的結果 由自己作爲一個同時測試的惟一
    標準測試。在測試之外,
    不會發生。

+0

更多討論:內置函數默認爲$ _( HTTP:// WWW。perlmonks.org/?node_id=606237) – toolic 2011-05-05 12:42:13

1

非常重要的一部分,其他的答案已經錯過爲該線

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

其實就是修改傳遞給函數的參數,而不是它們的副本。

grep是一個過濾命令,代碼塊內的$_中的值是@_中某個值的別名。 @_又包含傳遞給函數的參數的別名,所以當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} @_ 
} 

如果有重複的替代你的目標是執行排序與原始數據的一個短暫的修改應用,你應該看看一個叫做Schwartzian transform的Perl成語,它是實現這個目標的更有效的方式。

+0

我會看看這個習語,但是根據我的數據修改參數是可以接受的。 – Craig 2011-05-05 16:02:32

+0

@Craig =>修改參數是不可接受的,如果它是無意的。如果從現在開始幾個月你需要對常量數組或者將再次使用的數組進行排序呢? – 2011-05-05 20:19:41

+0

看到你的觀點,但它不會發生,我_did_說我會看看這個成語 – Craig 2011-05-09 12:56:24

2

很多人都回答正確的s運營商正在修改$_,但是在即將發佈的Perl 5.14.0會有爲s運營商(即s///r)的,而不是修改就地新r標誌將返回修改後的元素。閱讀更多在 The Effective Perler。您可以使用 perlbrew來安裝這個新版本。

編輯:Perl 5.14現在可用! AnnouncementAnnouncementDelta

這裏是通過畝(使用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 } 
      @_; 
}