2009-02-25 15 views
9

我想定義一個Perl函數(稱之爲「差異」),這取決於命令行參數。下面的代碼不起作用:我怎樣纔能有條件地定義一個Perl子程序?

if ("square" eq $ARGV[0]) {sub difference {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {sub difference {return 1}} 

看來,這個條件可以忽略,因此,無論是「差異化」功能得到$ ARGV [0]的值的第二個定義。

我可以把一個狀態到函數使代碼工作:

sub difference { 
    if ("square" eq $ARGV[0]) {return ($_[0] - $_[1]) ** 2} 
    elsif ("constant" eq $ARGV[0]) {return 1} 
} 

但是,這是不是真的我的本意 - 我不需要條件執行期間每次進行評估。我只需要一種方法來影響函數的定義。

我的問題是:

  1. 爲什麼先施工無法正常工作?
  2. 它爲什麼不給出一個錯誤,或其他一些跡象表明有什麼錯誤?
  3. 有沒有辦法在Perl中有條件地定義函數?
+0

當你處於這種狀態時,你可能想要確保你或者A)死亡,如果在你做這件事之前有無效的參數,或者B)有第三個定義,以免前兩個語句無效。如果你試着打電話給&差異,那就不好了,它不在那裏。 – 2009-02-25 23:28:09

+1

當你不「嚴格使用;全部使用警告」;「這些都是你遇到的問題。 – JDrago 2009-02-26 00:21:18

回答

24

其他人已經提出了你所要求的語法,但我會建議使用更明確的子程序引用,這樣你就可以自由地操作引用而不用操作定義。例如:

sub square_difference { return ($_[0] - $_[1]) ** 2 } 
sub constant_difference { return 1 } 

my %lookup = (
    'square' => \&square_difference, 
    'constant' => \&constant_difference, 
); 

my $difference = $lookup{$ARGV[0]} || die "USAGE: $0 square|constant\n"; 
print &$difference(4, 1), "\n"; 

這是相同的基本方法,但我認爲這句法會讓你映射參數更方便地爲你增加更多的每個子程序位。請注意,這是Strategy Pattern的變體,如果您遇到這種情況。

11

我沒有親自做了很多,但你可能想使用一個變量來保存子程序:

my $difference; 
if ("square" eq $ARGV[0]) {$difference = sub {return ($_[0] - $_[1]) ** 2}} 
elsif ("constant" eq $ARGV[0]) {$difference = sub {return 1}} 

調用具有:

&{ $difference }(args); 

或者:

&$difference(args); 

或者,如Leon Timmermans所建議的:

$difference->(args); 

的解釋有一點 - 這聲明瞭一個名爲$difference的變量,並根據您的情況,將其持有參考一個匿名子程序。因此,您必須將取消引用$difference作爲子程序(因此前面的&)才能調用子程序。

編輯:代碼測試和工作。

還有一個編輯:

耶穌,我習慣了use荷蘭國際集團strictwarnings,我忘了他們是可選的。

但嚴重。總是use strict;use warnings;。這將有助於解決這樣的問題,併爲您提供很好的有用的錯誤消息,以解釋發生了什麼問題。由於strictwarnings,我從來沒有必要在我的生活中使用調試器 - 這是錯誤檢查消息的好處。他們會抓住所有類似的東西,甚至給你有用的消息,爲什麼他們是錯的。

所以,請你隨時寫東西,無論多小(除非被模糊處理),總是use strict;use warnings;

+0

更好的語法是$ difference - >(args) – 2009-02-25 23:27:36

+0

我最喜歡這種方法 - 它使用本地引用符號並且不需要另一個散列表。實際上,通過這種方法或哈希表引用方法,您可以通過添加一個附加功能層(例如,將$ difference - >(args)包裝在一個簡單易用的函數中: sub diff(args) {difference - >(args); } 現在,您不必在主程序中使用箭頭(或替代)符號。 – bearvarine 2017-11-26 17:40:39

16

你想這樣做可以這樣實現什麼:

if ($ARGV[0] eq 'square') { 
    *difference = sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif ($ARGV[0] eq 'constant') { 
    *difference = sub { return 1 }; 
} 
+0

使用typeglob有點沉重,不是嗎?您可以使用$差異代替代碼參考;您可以使用&$差異(arg1,arg2)來調用它。 – 2009-02-26 04:24:59

+1

typeglob是做到這一點的方法。不過,我會把它放在BEGIN塊中。 (實際上,我認爲「問題」是braindead,並且不會像這樣有條件地編譯東西,但是除此之外...) – jrockway 2009-02-26 05:06:36

+0

@jrockway - 你爲什麼要把它放在BEGIN塊中?這是必要的,因爲任何特定的原因還是僅僅是你的個人風格? – 2009-02-26 05:09:26

6

替補在編譯時定義 - >如果您選擇了「使用警告」啓用後,你會看到大約子程序重新定義錯誤消息。

-2

還有一種方法:

my $diffmode; 
BEGIN { $diffmode = $ARGV[0] } 
sub difference { 
    if ($diffmode eq 'square') { ($_[0] - $_[1]) ** 2 } 
    elsif ($diffmode eq 'constant') { 1 } 
    else { "It don't make no never mind" } 
} 
0

感謝如何使代碼工作的所有建議。爲了完整起見,我會給出我的問題的高級答案。

  1. 因爲功能在編譯時定義,但條件和/或命令行參數在運行時被評估第一個結構不起作用。在評估條件時,已命名的功能已被定義。

  2. 編譯器確實給出了「使用警告」的警告,雖然對於程序員而言不是一個非常有用的警告,但它並不知道1 :-)給出有意義的警告的困難在於,在if語句中定義函數是有意義的如果你也在if語句中對函數做某些事情,就像Leon Timmermans的建議一樣。原始代碼編譯爲空的if語句,並且編譯器未設置爲警告這些。

  3. 嚴格來說,不可能有條件地定義函數,但可以有條件地將引用(rbright)或別名(Leon Timmermans)定義爲函數。共識似乎是引用比別名更好,但我不太清楚爲什麼。

關於1的注意事項:只有在實際遇到類似問題時,評估順序纔不明顯;人們可以設想一個Perl,它可以在編譯時評估條件,只要它可以安全地完成。顯然Perl不會這樣做,因爲下面的代碼也給出了一個關於重定義的子例程的警告。

use warnings ; 
if (1) {sub jack {}} else {sub jack {}} 
1

其他答案是正確的,使用代碼引用或別名。但是別名示例引入了yicky typeglob語法,並忘記處理strict。

Alias是一個被遺忘的模塊,它包裝了所有需要的參數,以保證嚴格的參考名稱。

use strict; 
use Alias; 

my $difference_method = $ARGV[0]; 
if("square" eq $difference_method) { 
    alias difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    alias difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

而現在difference($a, $b)的作品。

如果您只需在自己的代碼中調用difference()即ie。你不會將它作爲函數導出,我只是使用代碼引用並忘記別名。

my $difference_method = $ARGV[0]; 

my $Difference; 
if("square" eq $difference_method) { 
    $Difference => sub { return ($_[0] - $_[1]) ** 2 }; 
} 
elsif("constant" eq $difference_method) { 
    $Difference => sub { return 1 }; 
} 
else { 
    die "Unknown difference method $difference_method"; 
} 

$Difference->($a, $b); 

有條件地改變函數的功能會使代碼難以遵循,並且不夠靈活,就像改變任何全局行爲一樣。

my $Difference_Method = $ARGV[0]; 

sub difference { 
    if($Difference_Method eq 'square') { 
     return ($_[0] - $_[1]) ** 2; 
    } 
    elsif($Difference_Method eq 'constant') { 
     return 1; 
    } 
    else { 
     die "Unknown difference method $Difference_Method"; 
    } 
} 

你有形式的子程序任何時候...

sub foo { 
    if($Global) { 
     ...do this... 
    } 
    else { 
     ...do that... 
    } 
} 

你有一個問題:當你意識到你只是優化這一點,變得更加明顯。

別名對於在運行時使用閉包生成類似函數非常有用,而不是通過手工粘貼它們來減少&。但這是另一個問題。