簡體   English   中英

如何有條件地定義 Perl 子例程?

[英]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 的定義。

我的問題是:

  1. 為什么第一次施工不起作用?
  2. 為什么它沒有給出錯誤或其他一些表明有問題的跡象?
  3. 有沒有辦法有條件地定義 Perl 中的函數?

其他人已經提供了您要求的語法,但我建議為此使用更明確的子例程引用,以便您可以自由地操作引用而無需操作定義。 例如:

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 strictwarnings ,以至於我忘記了它們是可選的。

不過實話說。 始終use strict; use warnings; . 這將有助於捕捉這樣的事情,並為您提供有用的錯誤消息來解釋問題所在。 由於strictwarnings ,我在生活中從未使用過調試器——這就是錯誤檢查消息的好處。 他們會捕捉到各種各樣的事情,甚至會向您提供有用的信息,說明他們為什么錯了。

所以請,無論何時你寫的東西,無論多么小(除非它被混淆),總是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...
    }
}

你有問題。

別名對於在運行時使用閉包生成類似函數最有用,而不是手動剪切和粘貼它們。 但這是另一個問題。

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

  1. 第一個構造不起作用,因為函數是在編譯時定義的,但條件和/或命令行 arguments 是在運行時評估的。 在評估條件時,已定義名為 function 的名稱。

  2. 編譯器確實給出了一個帶有“使用警告”的警告,盡管對於不知道 1 的程序員來說這不是非常有用的:-) 給出有意義的警告的困難在於,如果你也做某事,在 if 語句中定義函數是有意義的如 Leon Timmermans 的建議,在 if 語句中使用 function。 原始代碼編譯為一個空的 if 語句,並且編譯器沒有設置為對這些發出警告。

  3. 嚴格來說,不可能有條件地定義函數,但可以有條件地定義對函數的引用(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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM