2012-01-16 54 views
8

在(非常感謝)perlmonks網站,我發現following snippet that trims的空間從一個字符串的兩邊:在Perl中,函數應該做wantarray跳舞,還是我們可以期待調用者使用map?

sub trim { 
    @_ = $_ if not @_ and defined wantarray; 
    @_ = @_ if defined wantarray; 
    for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } 
    return wantarray ? @_ : $_[0] if defined wantarray; 
} 

我不明白爲什麼筆者去檢查wantarray幾乎每行的所有麻煩。爲什麼不修剪字符串,並讓程序員在傳遞數組時使用map

那是什麼飾板之間的區別,被稱爲是這樣的:一個簡單的裝飾

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 
my @result = trim(@test_array); 

或者,像這樣調用時,一個需要整理磁盤陣列:

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 
my @result = map { trim($_) } @test_array; 
+0

爲什麼你想離開地圖給用戶,而你可以將它抽象出來? – 2012-01-16 10:03:19

+1

相關:[在Perl修剪](https://plus.google.com/105725977711317285348/posts/ienzxqHJmRe) – daxim 2012-01-16 11:33:15

+0

wantarray和類似魔屎是疫病。除非必須,否則不要做聰明的編程。永遠不要聰明的API。 – tsee 2012-01-16 20:02:29

回答

11

,首先它的更好,如果你抽象的是地圖遠:

#e.1. 
sub trim 
{ 
    my @ar = @_; 
    for (@ar) { s/^\s+//, s/\s+$// }; 
    return wantarray ? @ar : $ar[0]; 
} 

二,審議通過上面的例子,它具有比較:

#e.2. 
sub trim 
{ 
    for (@_) { s/^\s+//, s/\s+$// }; 
} 

有什麼區別?

e.1。返回一個新的修剪陣列,而e.2。修改原始數組。

現在好了,原始的神祕子程序是做什麼的?

它自動奇蹟般地(yeaah,它的Perl)修改原始數組如果你沒有返回值分配給任何或離開原數組不變,並返回一個新的修剪陣列如果你分配的返回值到另一個變量

怎麼樣?

通過檢查是否需要定義wantarray。只要該函數在右側並且返回值被分配給變量「defined wantarray」爲真(無論標量/數組上下文)。

+2

作者還希望函數在不向函數傳遞參數的情況下隱式操作'$ _'。 – mob 2012-01-17 00:18:05

+0

所以,你會建議讓每個函數都接受LIST和SCALAR,所以調用者不必自己寫地圖?我喜歡這個主意,但是有兩件事讓我失望:1.不需要時的開銷; 2.複製粘貼代碼(4行而不是1)只是爲了處理無效/標量/列表上下文。 – Konerak 2012-01-17 08:45:04

+0

不接受(無論如何,你的參數將全部進入子程序中的@_),但返回。並可以通過返回wantarray來實現? @ar:$ ar [0];聲明。原來的4班輪正在做更多的照顧標量/列表,因爲我試圖解釋上面。 – 2012-01-17 09:11:50

5

也許筆者想模仿標準chomp函數的行爲。沒有必要在你自己的功能中做到這一點。

man perlfunc 
chomp VARIABLE 
chomp(LIST) 
chomp 

[...]如果你的Chomp的列表,每個元素都chomped。 [...]

+0

我猜wantarray無關與這種行爲在格格 – 2012-01-16 10:48:37

+0

格格被修改originial陣列所以它可能是簡單的:子格格{爲(@_)S /等等/等等/} – 2012-01-16 10:51:53

+0

是啊,你是對。實際上'wantarray'在調用者期望一個標量時變得被定義。 – kubanczyk 2012-01-16 14:15:38

1

請注意,這正是Text::Trim的實現方式。請參閱各種使用案例的描述。使用wantarray進行播放可以區分各種上下文,併爲每個上下文實現不同的語義。

我個人更喜歡單一的語義,因爲它更容易理解和使用。我會避免使用$_默認變量或修改,符合尼龍微笑的示例1。

7

打破了由線下來行是因爲它一直沒有:

sub trim { 
    @_ = $_ if not @_ and defined wantarray; 
    # if there are no arguments, but a return value is requested 
    # then place a copy of $_ into @_ to work on 

    @_ = @_ if defined wantarray; 
    # if the caller expects a return value, copy the values in @_ into itself 
    # (this breaks the aliasing to the caller's variables) 

    for (@_ ? @_ : $_) { s/^\s+//, s/\s+$// } 
    # perform the substitution, in place, on either @_ or $_ depending on 
    # if arguments were passed to the function 

    return wantarray ? @_ : $_[0] if defined wantarray; 
    # if called in list context, return @_, otherwise $_[0] 
} 

我同意代碼變得有點乏味與所有wantarray檢查,但結果是共享一個級別的功能Perl內置函數的靈活性。使函數「聰明」的最終結果是清理調用站點(避免循環構造,臨時變量,重複...),這取決於函數的使用頻率,可以有效地提高代碼的可讀性。

功能可以簡化一點:

sub trim { 
    @_ = @_ ? @_ : $_ if defined wantarray; 

    s/^\s+//, s/\s+$// for @_ ? @_ : $_; 

    wantarray ? @_ : shift 
} 

前兩行可以集於一身,因爲他們都在做同樣的事情(分配給@_)只是用不同的源值。並且最後不需要外部return ... if defined wantarray檢查,因爲在void上下文中返回值無論如何都不會執行任何操作。

但我可能會在最後一行更改爲​​,因爲這使得它像一個列表(在標量上下文最後一個元素)。

一旦所有說,做,這允許使用下列調用樣式:

my @test_array = ('string1', ' string2', 'string3 ', ' string4 '); 

my @result = trim @test_array; 
my $result = trim $test_array[0]; 
trim @test_array; # in place trim 

,甚至還支持調用現場循環:

my @result = map trim, @test_array; 

以上冗長爲:

my @result = map trim($_), @test_array; 

它可以在類似於的while循環中使用3210

while (<$file_handle>) { 
    trim; 
    # do something 
} 

關於dwimmery在Perl中的觀點有很多。我個人喜歡它,當函數爲我提供了靈活的編碼調用方法,而不是圍繞函數的嚴格界面工作。

相關問題