簡體   English   中英

Perl 子程序參數

[英]Perl subroutine arguments

我最近一直在閱讀有關 Perl 的文章,但對 Perl 如何處理傳遞給子例程的參數感到有些困惑。

在 Python、Java 或 PHP 等語言中,函數定義采用以下形式(偽代碼):

function myFunc(arg1, arg2) {
    // Do something with arg1 and arg2 here
}

然而在 Perl 中,它只是:

sub mySub {
    # @_ holds all arguments passed
}

據我所知,這是唯一的方法。

  • 如果我想限制調用者只傳遞兩個參數怎么辦?

  • 這難道不是 Perl 只允許其他語言(即 Python、C 等)中的可變數參數以外的任何東西嗎?

  • 這在某些時候不會成為問題嗎?

  • 其他語言中的所有默認參數編號檢查怎么樣? 是否必須在 Perl 中明確地做到這一點? 例如

     sub a_sub { if (@_ == 2) { # Continue function } else { return false } }

您對 Perl 環境持謹慎態度,因為它與您之前遇到的語言大不相同。

相信強類型和函數原型的人在這里會不同意,但我相信這樣的限制很少有用。 C 是否真的發現您經常向函數傳遞錯誤數量的參數以致於有用?

在現代 Perl 中最常見的是將@_的內容復制到詞法標量變量列表中,因此您經常會看到子程序以

sub mysub {
  my ($p1, $p2) = @_;
  ... etc.
}

這樣,所有傳遞的參數都可以作為@_$_[0]$_[1]等)的元素使用,而預期的參數被命名並出現在$p1$p2 (盡管我希望你理解應該適當地選擇這些名稱)。

在子程序是方法的特殊情況下,第一個參數是特殊的。 在其他語言中,它是selfthis ,但在 Perl 中,它只是@_的第一個參數,您可以@_稱呼它。 在這些情況下你會看到

sub method {
  my $self = shift;
  my ($p1, $p2) = @_;
  ... etc.
}

以便上下文對象(或類的名稱,如果它是類方法)被提取到$self (約定假定的名稱)中,其余參數保留在@_以便直接訪問,或者更常見的是, 復制到局部標量變量$p1 , $p2等。

最常見的抱怨是也沒有類型檢查,所以我可以傳遞任何我喜歡的標量作為子程序參數。 只要use strictuse warnings在上下文中,即使這樣通常也很容易調試,僅僅是因為子例程可以對一種形式的標量執行的操作在另一種形式上通常是非法的。

雖然它最初更多地與面向對象 Perl 的封裝有關,但 Larry Wall 的這句話非常相關

Perl 不迷戀強制隱私。 它寧願你呆在客廳外面,因為你沒有被邀請,而不是因為它有一把獵槍

C 的設計和實現是在那個時代,如果您可以讓錯誤的程序在編譯期間而不是在運行時失敗,它會大大提高效率。 現在已經改變,雖然類似的情況已經與客戶端JavaScript出現在那里,它實際上知道的代碼是從它必須處理與互聯網獲取數據之前錯誤有用。 可悲的是,JavaScript 參數檢查現在比它應該的要松散。


更新

對於那些懷疑 Perl 用於教學目的的人,我建議正是因為Perl 的機制非常簡單和直接,所以它們非常適合此類目的。

  • 當您調用 Perl 子例程時,調用中的所有參數都以@_別名 您可以直接使用它們來影響實際參數,或者復制它們以防止外部動作

  • 如果您將 Perl 子例程作為方法調用,則調用對象或類將作為第一個參數提供。 同樣,子程序(方法)可以用@_做它喜歡做的事

Perl 不會為您管理參數處理。 相反,它提供了一個最小的、靈活的抽象,並允許您編寫適合您需要的代碼。

通過引用

默認情況下,Perl 為@_每個參數添加一個別名。 這實現了基本的按引用傳遞語義。

my $num = 1;
foo($num);
print "$num\n";  # prints 2.

sub foo { $_[0]++ }

通過引用傳遞速度很快,但存在泄露參數數據更改的風險。

通過復制

如果您想通過復制語義傳遞,您需要自己制作副本。 處理位置參數列表的兩種主要方法在 Perl 社區中很常見:

sub shifty {
    my $foo = shift;
}

sub listy {
    my ($foo) = @_;
}

在我工作的地方,我們做了一個 listy 版本:

sub fancy_listy {

    my ($positional, $args, @bad) = @_;

    die "Extra args" if @bad;
}

命名參數

另一種常見做法是使用命名參數

sub named_params {
    my %opt = @_;
}

有些人對以上內容感到滿意。 我更喜歡更詳細的方法:

sub named_params {
    my %opt = @_;

    my $named = delete $opt{named} // "default value";
    my $param = delete $opt{param}
        or croak "Missing required 'param'";

    croak "Unknown params:", join ", ", keys %opt
        if %opt;

    # do stuff 
}

這將命名參數解包為變量,為基本驗證和默認值留出空間,並強制不傳入額外的未知參數。

Perl 原型

Perl 的“原型”不是通常意義上的原型。 它們只提供編譯器提示,允許您跳過函數調用的括號。 唯一合理的用途是模仿內置函數的行為。 您可以輕松擊敗原型參數檢查。 一般來說,不要使用原型 小心使用它們,因為您會使用運算符重載——即謹慎使用,僅用於提高可讀性。

出於某種原因,Perl 喜歡列表,而不喜歡靜態類型。 @_數組實際上提供了很大的靈活性,因為子程序參數是通過引用傳遞,而不是通過值傳遞。 例如,這允許我們做out-arguments:

my $x = 40;
add_to($x, 2);
print "$x\n"; # 42

sub add_to { $_[0] += $_[1] }

……但這更像是一個歷史性的性能黑客。 通常,參數由列表賦值“聲明”:

sub some_sub {
  my ($foo, $bar) = @_;
  #               ^-- this assignment performs a copy
  ...
}

這使得這個子按值調用的語義,這通常是更可取的。 是的,未使用的參數只是被遺忘了,太少的參數不會引發任何自動錯誤——變量只包含undef 您可以添加任意驗證,例如通過檢查@_的大小。


有計划在未來最終使命名參數可用,看起來像

sub some_sub($foo, $bar) { ... }

如果您安裝了signatures模塊,您今天就可以使用這種語法。 但還有更好的東西:我強烈推薦Function::Parameters ,它允許像這樣的語法

fun some_sub($foo, $bar = "default value") { ... }

method some_method($foo, $bar, :$named_parameter, :$named_with_default = 42) {
  # $self is autodeclared in methods
}

這也支持實驗類型檢查。

解析器擴展 FTW!

如果您真的想在 Perl 中強加更嚴格的參數檢查,您可以查看類似Params::Validate 的內容

Perl 確實具有參數占位符的原型設計功能,您已經習慣了這種功能,但通常沒有必要。

sub foo($){
    say shift;
}; 
foo();      # Error: Not enough arguments for main::foo
foo('bar'); # executes correctly

如果你做了sub foo($$){...}它將需要 2 個非可選參數(例如foo('bar','baz')

你可以只使用:

my ($arg1, $arg2) = @_;

要明確限制您可以使用的參數數量:

my $number =2;
die "Too many arguments" if @_ > $number;

如果您最近正在閱讀 Perl,請閱讀最近的 Perl。 您也可以免費閱讀Modern Perl書籍。

以下是該書中關於函數簽名的一些示例:

sub greet_one($name = 'Bruce') {
    say "Hello, $name!";
}
sub greet_all($leader, @everyone) {
    say "Hello, $leader!";
    say "Hi also, $_." for @everyone;
}

sub make_nested_hash($name, %pairs) {
    return { $name => \%pairs };
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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