2009-10-15 25 views
5

所以我有一個簡單的Perl函數,我已經有一段時間了,並且想要擴展它,這是我想到的,這是我應該建立我的函數來處理可選參數?這是構建Perl子例程的方式嗎?

原文:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

擴展:

sub ucwords{ 
    if(@_[0] ne undef){#make sure some argument was passed 
     @overloads = (0,1,2,3); 
     $str = @_[0]; 
     if(@_[1] eq undef || @_[1] eq 0){ #default is to lowercase all but first 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }else{ #second parameters 
      if(!grep $_ eq @_[1], @overloads){ die("No overload method of ucwords() takes "[email protected]_[1]." as second parameter."); } 
      if(@_[1] eq 1){ $str =~ s/\b(\w)/\u$1/g;} #first letter to upper, remaining case maintained 
      if(@_[1] eq 2){ $str = lc($str); $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining to lower 
      if(@_[1] eq 3){ $str =~ s/(\w)\b/\u$1/g;} #last letter to upper, remaining case maintained 
      return $str; 
     } 
    }else{ 
     die("No overload method of ucwords() takes no arguments"); 
    } 
} 

精神科

+5

tl; dr,但是不是你的代碼只是做這個? 'join''map {ucfirst} split /(\ s +)/ $ string'? – Ether 2009-10-15 16:42:48

+3

@Psytronic你應該閱讀'perldoc perlsub':http://perldoc.perl.org/perlsub.html – 2009-10-15 16:45:25

+0

感謝您的建議,我並不特別尋找關於功能本身的評論,但我知道有可能更好做這項工作的方式,這是關於我建立它的方式的更多評論,因爲我不想從一開始就做錯事,反覆重複。 – Psytronic 2009-10-16 07:23:05

回答

25

在一個字:NO!

讓我們看一下:

sub ucwords{ 
    $str = @_[0]; 
    $str = lc($str); 
    $str =~ s/\b(\w)/\u$1/g; 
    return $str; 
} 

首先,不使用strict。用它。這是爲了你自己的利益。

其次,您沒有使用warnings。用它。這是爲了你自己的利益。例如,@_的第一個元素應該使用$_[0]而不是@_[0]

第三,你應該得到的再次重新發明輪子之前偶爾閱讀常見問題列表的習慣:見How do I capitalize all the words on one line?

如果你認爲這是嚴酷的,考慮的是,當被稱爲:

print ucwords("FRED AND BARNEY'S LODGE"), "\n"; 

代碼輸出

 
Fred And Barney'S Lodge 

這是在這個問題給出的例子。

此外,有一個功能可以完成多件事情,根據神祕數字選擇它所做的事情,這些事情都不是正確的設計策略。

您應該擁有多個函數,它們的命名方式可以由您的代碼的臨時讀者來理解,其中每個函數只做一件事並且做對。

最後,你的功能的擴展版本(沒有說如何編寫這樣的功能的智慧的任何東西)可以更好地寫爲:

# untested code follows 

use Carp; 

{ 
    my %modes = map {$_ => undef} 0 .. 3; 
    sub ucwords{ 
     croak 'No arguments passed' unless @_; 

     my ($str, $mode) = @_; 
     $mode = 0 unless defined $mode; 

     croak "Invalid mode: '$mode'" unless exists $modes{$mode}; 

     if ($mode == 0) { 
      $str = lc($str); 
      $str =~ s/\b(\w)/\u$1/g; 
     } 
     elsif ($mode == 1) { 
      $str =~ s/\b(\w)/\u$1/g;   
     } 
     elsif ($mode == 2) { 
      $str = lc($str); 
      $str =~ s/(\w)\b/\u$1/g;   
     } 
     else { 
      $str =~ s/(\w)\b/\u$1/g; 
     } 

     return $str; 
    } 
} 

參見Why use if-else if in C++?

+4

我認爲你應該在這種情況下回答兩次。一次使用原始答案,一次使用當前版本。在這種情況下,你應該可以從我那裏得到兩個贊同。 – innaM 2009-10-15 16:03:09

+0

我同意它應該是另一個答案,但我不知道我是否會upvoted第一個,因爲只是說使用嚴格和警告並沒有真正指出功能的其他問題。無論哪種方式,這一個+1。 :) – NateDSaint 2009-10-15 16:12:16

+1

它應該不言而喻......但作爲在我的工作中維護腳本的人,我要說的是......如果您要使用類似上述擴展功能的東西 - DOCUMENT,如果僅在評論中給你的模式命名。 – Rini 2009-10-15 16:42:11

4

可能你會發現Params::Validate有用。它可以用來通過各種規則驗證參數。以下是它在您的情況下的外觀:

## somewhere is the start of the module 
use Params::Validate qw(:all); 

sub ucwords { 
    ## this line helps to undestand which parameter should be passed to function 
    my ($string, $algorithm_id) = @_; 

    ## make sure that 2 scalar parameters passed 
    validate_pos(@_, {'type' => SCALAR}, {'type' => SCALAR}); 

    ## main code here 
} 
11

不要使用$foo ne undef構造。 Perl中的操作符稱爲「上下文敏感」。通過使用某些操作符,可以引入特定的上下文。neeqltgtlege都是 「串」 操作符,處理標量在任一側上爲字符串,而==!=<><=>=是數值運算,處理所述對象上的任一側作爲一個號碼。

但是,如果你正在爲民主基金測試,它確實沒有意義的東西undefined是一個數字或字符串,使他們有一個操作員只爲那種測試:defined

你可以如果測試的東西是做

if (defined $foo) { 
    # my cool logic on $foo here 
} 
5

簡單地定義這可能只是我的意見,你的編碼風格是完全取決於你,但我個人在馬上蝙蝠分配參數變量發現了很多價值,而不是把你的子程序的「業務」部分包裝在一個if塊中,我可以使用函數croak那個。例如:

use Carp; 

sub ucwords { 
    my $str = shift; 
    defined($str) 
     or croak 'No overload method of ucwords() takes no arguments'; 
    #the rest of your logic 
} 
+0

這是一個很好的觀點。我會編輯原文,但這會讓您的評論顯得毫無意義。我會爲它添加一個編輯。 – NateDSaint 2009-10-15 16:13:12

+1

@NateDSaint:我冒昧編輯您的文章並刪除我的評論。如果您不喜歡更改,請隨時回滾。 – 2009-10-15 16:26:43

+0

這種方式的誤導可能較少。謝謝! – NateDSaint 2009-10-15 16:35:08

4

die

die,像其他的perl建宏,並不需要,一般不應該有括號。然而,die有一個大哥哥,大多數人使用這些天,被稱爲

croak

務必:

use Carp; 

然後

croak "My error here!"; 

呱 - 呱作品就像模具,但一般比die所做的錯誤消息更多有用的信息,例如錯誤發生在rel的行給呼叫者提供服務。

3

數組索引

數組訪問,如同Perl中其他的事情,是上下文敏感的。考慮一下附加在名稱上的標籤,作爲提醒您當前正在訪問或使用的內容。無論何時你看到$,這意味着你正試圖獲得一個標量值。無論何時你看到一個@,這意味着你正在訪問一個列表,而%當然意味着一個鍵/值哈希對。所以,當你像這樣訪問你的數組時:

@_[1] 

你在詢問一個包含單個元素的列表。此功能可讓您一次從數組中獲取多個值,但只訪問一個值時,會在某些情況下(例如分配)造成問題。因此,訪問一個數組元素時,要始終使用標量上下文:

$_[1] 
5

Perl的switch語句:給定/時

的Perl,爲5.10及以上,擁有一個夢幻般的開關內置語句,稱爲[given]。這大致相當於C語言中的switch聲明,但更通用。要啓用此功能,你需要在你的腳本的頂部添加一行:

use 5.010; 

這使得所有的Perl 5.10的功能,其中包括開關(和say,其工作方式print但自動添加一個「\ n 「結尾)你可以使用它是這樣的:

my $foo = get_foo(); 
my $nothing = 0; 
given($foo) { 
    when (undef) { say "got an undefined value!"; } 
    when ([1,3,5,6,8]) { say "was 1, 3, 5, 6, or 8"; } 
    when (/^abc/) { say "was a string starting with abc"; } 
    when ($_ == 4) { say "It was 4!!!"; } 
    when ($_ > 100) { say "Greater than 100"; } 
    default { $nothing = 1; } 
} 

傳遞給定自動被投入$_給定的代碼裏面,讓您比較反對它的變量。然後,when構造做對$_.智能匹配所以,在你的情況下,它應該是這樣的(固定@[]以 $ []號):

given ($_[1]) { 
    when (1) { $str =~ s/\b(\w)/\u$1/g } 
    when (2) { $str = lc($str); $str =~ s/(\w)\b/\u$1/g } 
    when (3) { $str =~ s/(\w)\b/\u$1/g; } 
    default { croak "No overloaded method of ucwords() takes '$_'." } 
} 
5

@_開箱

通常,在你的子程序中做任何其他處理之前,你總是希望解壓@_。這使得用戶,其他維護人員以及將來自己對如何使用您的子系統更加清楚。通過直接使用@_,僅僅根據給出的參數很難弄清楚需要傳遞什麼。他們沒有任何有意義的名字,使得確定他們的目的變得更加困難,而且你到處都有魔法常量 - 通常總體上是一件壞事!

最好的辦法是在你做其他事情之前馬上把變量變成有意義的標量。

對於一個參數子程序,常見的解決方案是使用shift。這會關閉數組的第一個元素,並返回它(類似於pop的反面)。如果沒有給出數組,並且您處於子例程中,它會將其從@_數組中取出。所以,你可以做

sub mysub { 
    my $foo = shift; 
} 

任何一個參數的子程序。

但是,如果你有更多?列出上下文分配,以幫助!可以使用列表分配一次分配多個變量。你可以做

sub myothersub { 
    my ($foo, $bar, $baz) = @_; 
} 

而且$foo$bar$baz將在@_ 0,1,2指標,分別被分配的值。那麼,如果0,1或2索引中沒有任何內容會發生什麼?他們仍然被分配 - 他們變成了undef!然後你可以檢查這個問題中其他地方提到的undef。

2

我非常不喜歡過於智能的功能。過度智能的功能是其行爲完全由其參數改變的功能。看看你的,他們幾乎不分享任何代碼,除了參數處理。無論如何,如果我會做一些類似這樣我會寫這樣的事:那是在被暗示,但在其他的答案沒有直接涉及

use Carp; 

{ 
    my %ucwords = (
     0 => sub { 
      my $str = lc(shift()); 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     1 => sub { 
      my $str = shift; 
      $str =~ s/\b(\w)/\u$1/g; 
      return $str; 
     }, 
     2 => sub { 
      $str = lc(shift()); 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     }, 
     3 => sub { 
      my $str = shift; 
      $str =~ s/(\w)\b/\u$1/g; 
      return $str; 
     } 
    ); 

    sub ucwords { 
     my ($str, $mode) = @_; 
     croak "No overload method of ucwords() takes no arguments" 
      unless defined $str; 
     $mode = 0 unless defined $mode; 
     my $code = $ucwords{$mode}; 
     croak "Invalid mode: '$mode'" unless defined $code; 
     goto \&$code; 
    } 
} 
2

東西是採用數字模式,外國人到Perl中的約定因溫度保持在。快速,無需查看代碼模式#3做什麼?地獄,看代碼模式#3做什麼?

Perl具有高效且易於使用的字符串。使用它們。給你的模式名稱與它的做法有關。像......第一,最後,recase_first,recase_last。它們不必是完全描述性的,lower_case_then_uc_last_letter的輸入時間可能太長,但足以爲人類大腦提供一些東西來吸引和聯想。

但實際上這些都是四個子程序。模式標誌是紅色的標誌,特別是當你的代碼大部分在if/else語句中。

相關問題