跳轉到內容

Raku 程式設計/子例程

來自華夏公益教科書,開放書籍,開放世界

子例程

[編輯 | 編輯原始碼]

在程式碼重用方面,最基本的構建塊是子例程。然而,它們並不是工具包中的唯一構建塊:Raku 還支援 *方法* 和 *子方法*,我們將在討論類和物件時進行討論。

子例程使用 `sub` 關鍵字建立,後跟名稱、可選引數列表,最後是一個程式碼塊。

程式碼塊

[編輯 | 編輯原始碼]

程式碼塊是包含在 `{}` 花括號中的程式碼組。程式碼塊有許多用途,包括將程式碼分開,將多個語句分組在一起以及為變數建立作用域。

定義子例程

[編輯 | 編輯原始碼]

子例程使用 `sub` 關鍵字定義。這是一個示例

sub mySubroutine () {
}

圓括號用於定義子例程的形式引數列表。引數就像普通的 `my` 區域性變數,不同之處在於它們在呼叫子例程時用值初始化。子例程可以使用 `return` 關鍵字將結果傳遞迴呼叫者

sub double ($x) {
    my $y = $x * 2;
    return $y;
}

可選引數

[編輯 | 編輯原始碼]

可選引數在其後有一個 `?`。此外,可選引數可以使用 `=` 給定預設值。必需引數可能在其後有一個 `!`,儘管這是位置引數的預設值。所有必需引數都必須列在所有可選引數之前。

sub foo (
    $first,       # First parameter, required
    $second!,     # Second parameter, required
    $third?,      # Third parameter, optional (defaults to undef)
    $fourth = 4   # Fourth parameter, optional (defaults to 4)
)

命名引數

[編輯 | 編輯原始碼]

普通引數按其位置傳遞:第一個傳遞的引數進入第一個位置引數,第二個進入第二個,依此類推。但是,還有一種方法可以透過名稱傳遞引數,並且可以以任何順序傳遞。命名引數基本上是鍵值對,其中一個字串名稱與一個數據值相關聯。可以使用鍵值對或副詞語法傳遞命名資料值。

sub mySub(:name($value), :othername($othervalue))

當然,子例程簽名允許使用一種特殊的簡寫方式,如果你變數與鍵值對的名稱相同,則可以使用它

sub mySub(:name($name), :othername($othername))

sub mySub(:$name, :$othername)      # Same!

在子例程宣告中,命名引數必須放在所有必需和可選位置引數之後。命名引數預設情況下被視為可選引數,除非在其後有一個 `!`。實際上,你也可以在必需位置引數之後放一個 `!`,但那是預設設定。

sub mySub(
    :$name!,             # Required
    :$type,              # Optional
    :$method?            # Still optional
)

貪婪引數

[編輯 | 編輯原始碼]

Raku 還允許使用 `*@` 語法使用所謂的“貪婪”引數。

sub mySub($scalar, @array, *@theRest) {
  say "the first argument was: $scalar";
  say "the second argument was: " ~ @array;
  say "the rest were: " ~ @theRest;
}

`*@` 告訴 Raku 將剩餘的引數展平到一個列表中並存儲在陣列 `@theRest` 中。這是為了允許 perl 接受位置或命名陣列,而不需要引用。

  my $first = "scalar";
  my @array = 1, 2, 3;
  mySub($first, @array, "foo", "bar");

上面的程式碼將輸出三行

  • 第一個引數是:標量
  • 第二個引數是:1, 2, 3
  • 其餘的是:"foo", "bar"

`return` 和 `want`

[編輯 | 編輯原始碼]

呼叫子例程

[編輯 | 編輯原始碼]

一旦我們定義了一個子例程,我們就可以在以後呼叫它來獲取結果或操作。我們已經看到了內建的 `say` 函式,你可以在其中傳遞字串,並將這些字串列印到控制檯。我們可以使用上面的 `double` 函式來計算各種值

my $x = double(2);       # 4
my $y = double(3);       # 6
my $z = double(3.5);     # 7

我們可以使用 `&` 符號將對子例程的引用儲存在普通標量變數中

my $sub = &double;
my $x = $sub(7)          # 14

多型子例程

[編輯 | 編輯原始碼]

在這個例子中,你可以看到我們同時向 `double` 子例程傳遞了整數值和浮點值。但是,我們可以使用型別說明符來限制哪些型別的值

sub double (Int $x) {    # $x can only be an int!
   return $x * 2;
}

my $foo = double(4);     # 8
my $bar = double(1.5);   # Error!

Raku 允許你編寫多個具有相同名稱的函式,只要它們具有不同的引數簽名並且用 `multi` 關鍵字標記。這被稱為 *多方法排程*,是 Raku 程式設計的一個重要方面。

multi sub double(Int $x) {
    my $y = $x * 2;
    say "Doubling an Integer $x: $y";
    return $x * 2;
}

multi sub double(Num $x) {
    my $y = $x * 2;
    say "Doubling a Number $x: $y";
    return $x * 2;
}

my $foo = double(5);        # Doubling an Integer 5: 10
my $bar = double(3.5);      # Doubling a Number 3.5: 7

匿名子例程

[編輯 | 編輯原始碼]

我們不需要像正常情況下那樣命名子例程,而是可以定義一個 *匿名子例程* 並將對它的引用儲存在一個變數中。

my $double = sub ($x) { return $x * 2; };
my $triple = sub ($x) { return $x * 3; };

my $foo = $double(5);     # 10
my $bar = $triple(12);    # 36

請注意,我們還可以將這些程式碼引用儲存在一個數組中

my @times;
@times[2] = sub ($x) { return $x * 2; };
@times[3] = sub ($x) { return $x * 3; };

my $foo = @times[2](7);     # 14
my $bar = @times[3](5);     # 15
華夏公益教科書