[英]How can I conditionally define a Perl subroutine?
我想定義一個取決於命令行參數的 Perl function (稱之為“差異”)。 以下代碼不起作用:
if ("square" eq $ARGV[0]) {sub difference {return ($_[0] - $_[1]) ** 2}}
elsif ("constant" eq $ARGV[0]) {sub difference {return 1}}
似乎忽略了條件,因此無論 $ARGV[0] 的值如何,“差異”function 都會獲得第二個定義。
我可以通過將條件放入 function 來使代碼工作:
sub difference {
if ("square" eq $ARGV[0]) {return ($_[0] - $_[1]) ** 2}
elsif ("constant" eq $ARGV[0]) {return 1}
}
但這並不是我的真正意圖——我不需要在執行期間每次都評估條件。 我只需要一種方法來影響 function 的定義。
我的問題是:
其他人已經提供了您要求的語法,但我建議為此使用更明確的子例程引用,以便您可以自由地操作引用而無需操作定義。 例如:
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";
這是相同的基本方法,但我認為這種語法將讓您 map arguments 更方便地添加到每個子程序中。 請注意,這是Strategy Pattern的一個變體,如果你喜歡這種東西的話。
你想要做的可以這樣實現:
if ($ARGV[0] eq 'square') {
*difference = sub { return ($_[0] - $_[1]) ** 2 };
}
elsif ($ARGV[0] eq 'constant') {
*difference = sub { return 1 };
}
我個人並沒有做過很多這樣的事情,但是您可能想使用一個變量來保存子例程:
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
strict
和warnings
,以至於我忘記了它們是可選的。
不過實話說。 始終use strict;
並use warnings;
. 這將有助於捕捉這樣的事情,並為您提供有用的錯誤消息來解釋問題所在。 由於strict
和warnings
,我在生活中從未使用過調試器——這就是錯誤檢查消息的好處。 他們會捕捉到各種各樣的事情,甚至會向您提供有用的信息,說明他們為什么錯了。
所以請,無論何時你寫的東西,無論多么小(除非它被混淆),總是use strict;
並use warnings;
.
子程序是在編譯時定義的 -> 如果您啟用了“使用警告”,您會看到有關子程序重新定義的錯誤消息。
其他答案是正確的,使用代碼參考或別名。 但是別名示例引入了 yicky typeglob 語法並且忘記了處理嚴格。
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()
,即。 您不會將其導出為 function,我只會使用代碼引用並忘記別名。
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);
有條件地更改 function 所做的事情會使代碼更難遵循且靈活性降低,就像在任何全局上更改行為一樣。 當您意識到您只是在優化它時,它變得更加明顯:
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...
}
}
你有問題。
別名對於在運行時使用閉包生成類似函數最有用,而不是手動剪切和粘貼它們。 但這是另一個問題。
感謝所有關於如何使代碼工作的建議。 為了完整起見,我將給出我的問題的高級答案。
第一個構造不起作用,因為函數是在編譯時定義的,但條件和/或命令行 arguments 是在運行時評估的。 在評估條件時,已定義名為 function 的名稱。
編譯器確實給出了一個帶有“使用警告”的警告,盡管對於不知道 1 的程序員來說這不是非常有用的:-) 給出有意義的警告的困難在於,如果你也做某事,在 if 語句中定義函數是有意義的如 Leon Timmermans 的建議,在 if 語句中使用 function。 原始代碼編譯為一個空的 if 語句,並且編譯器沒有設置為對這些發出警告。
嚴格來說,不可能有條件地定義函數,但可以有條件地定義對函數的引用(rbright)或別名(Leon Timmermans)。 共識似乎是引用比別名更好,盡管我不太清楚為什么。
關於 1 的注意事項:在您實際遇到這樣的問題之前,評估順序並不明顯; 可以設想一個 Perl,只要可以安全地完成,它就會在編譯時評估條件。 顯然 Perl 沒有這樣做,因為下面的代碼也給出了關於重新定義的子程序的警告。
use warnings ;
if (1) {sub jack {}} else {sub jack {}}
還有一種方式:
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" }
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.