跳轉到內容

Raku 程式設計/塊和閉包

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

關於塊

[編輯 | 編輯原始碼]

當我們談論子程式時,我們看到子程式宣告由三個部分組成:子程式名稱、子程式引數列表和子程式內部程式碼塊。塊在 Raku 中非常基礎,我們現在將使用它們來做各種有趣的事情。

我們已經看到了一些在各種結構中使用的塊

# if/else statements
if $x == 1 {
}
else {
}

# subroutines
sub thisIsMySub () {
}

# loops
for @ary {
}

loop (my $i = 0; $i <= 5; $i++) {
}

repeat {
} while $x == 1;

所有這些塊都用於將程式碼行分組在一起以用於特定目的。在 if 塊中,當 if 條件為真時,塊內的所有語句都會被執行。如果條件為假,整個塊將不被執行。在迴圈中,迴圈塊中的所有語句都會一起重複執行。

作用域

[編輯 | 編輯原始碼]

除了將類似的程式碼放在一起之外,塊還引入了作用域的概念。在塊內定義的 my 變數在塊外不可見。作用域確保變數僅在需要時使用,並且在不應修改時不修改它們。塊不需要與任何特定結構相關聯,例如 ifloop。塊可以獨立存在

my $x = 5;
my $y = 5;
{
   my $y = 3;
   say $x;         # 5
   say $y;         # 3
}
say $x;            # 5
say $y;            # 5

該示例很好地展示了作用域的概念:塊內的變數 $y 與塊外的變數 $y 不同。即使它們具有相同的名稱,但它們具有不同的作用域。這是一個稍微不同的示例

my $x = 5;
{
   my $y = 7;
   {
      my $z = 9;
      say $x;  # 5
      say $y;  # 7
      say $z;  # 9
   }
   say $x;     # 5
   say $y;     # 7
   say $z;     # ERROR: Undeclared variable!
}
say $x;        # 5
say $y;        # ERROR! Undeclared variable!
say $z;        # ERROR! Undeclared variable!

變數 $x 從其定義的位置開始,在其定義的作用域內的所有作用域內都是可見的。$y 僅在其定義的塊內以及該塊內的塊內可見。$z 僅在最內部的塊內可見。

作用域變數

[編輯 | 編輯原始碼]

在存在歧義的情況下,可以精確地指定作用域。我們可以使用 OUTER 這樣的關鍵字來指定來自當前作用域直接上方的作用域的變數

my $x = 5;
{
   my $x = 6;
   say $x;           # 6
   say $OUTER::x    # 5
}

子程式可以使用 CALLER 作用域訪問其被呼叫的作用域,假設外部作用域中的變數被宣告為 is context

my $x is context = 5;
mySubroutine(7);

sub mySubroutine($x) {
   say $x;         # 7
   say $CALLER::x; # 5
}

程式碼引用

[編輯 | 編輯原始碼]

塊可以儲存在一個單獨的標量變數中作為程式碼引用。一旦儲存在程式碼引用變數中,塊就可以像常規子程式引用一樣執行

my $dostuff = {
   print "Hello ";
   say "world!";
}

$dostuff();

我們在上面的示例中看到,塊可以儲存在變數中。此操作會建立一個閉包。閉包是儲存的程式碼塊,它儲存其當前狀態和當前作用域,這些作用域可以稍後訪問。讓我們看看閉包在實踐中的應用

my $block;
{
    my $x = 2;
    $block = { say $x; };
}
$block();   # Prints "2", even though $x is not in scope anymore

閉包在建立閉包時儲存了對 $x 變數的引用。即使該變數在程式碼塊執行時不再處於作用域中。

當我們稍後更改 $x 時,閉包將看到更改的值,因此如果您想建立多個包含不同封閉變數的閉包,則每次必須建立一個新變數

my @times = ();
for 1..10 {
   my $t = $_;          # each subroutine gets a different $t
   @times[$_] = sub ($a) { return $a * $t; };
}

say @times[3](4);       # 12
say @times[5](20);      # 100
say @times[7](3);       # 21

尖括號塊

[編輯 | 編輯原始碼]

我們可以使用 sub 關鍵字來建立子程式或子程式引用。這並不是唯一的方法,實際上對於匿名(“匿名”)子程式或子程式引用的常見情況,它有點過於冗長。對於這些情況,我們可以使用一種稱為尖括號塊的結構。尖括號塊在其他語言中稱為lambda 塊,非常有用。它們可以像匿名子程式一樣建立程式碼引用,並且還可以建立帶有引數的程式碼塊。尖括號塊非常像一個未命名的子程式。更一般地,它就像一個帶有引數的塊。我們在討論迴圈時已經簡要地看到了尖括號塊。我們在與迴圈結構相關的使用尖括號塊,以便為迴圈變數命名,而不是依賴預設變數 $_。這就是我們在這些情況下使用尖括號塊的原因:它們使我們能夠指定變數名稱作為任意程式碼塊的引數。

我們將展示一些示例

my @myArray = (1, 2, 3, 4, 5, 6);

# In a loop:
for @myArray -> $item {
    say $item;

# Output is:
#    1
#    2
#    3
#    4
#    5
#    6

}
# In a loop, multiples
for @myArray -> $a, $b, $c {
    say "$a, $b, $c";

# Output is:
#    1, 2, 3
#    4, 5, 6

}
# As a condition:
my $x = 5;
if ($x) -> $a { say $a; }  # 5
# As a coderef
my $x = -> $a, $b { say "First: $a.  Second: $b"; }
$x(1, 2);       # First: 1, Second: 2
$x("x", "y");   # First: x, Second: y
# As an inline coderef
-> $a, $b { say "First: $a, Second: $b"; }(1, 2)
#In a while loop
while ($x == 5) -> $a {
   say "Boolean Value: $a";
}

佔位符引數

[編輯 | 編輯原始碼]

在塊中,如果我們不想費力地寫出引數列表,我們可以使用佔位符引數。佔位符使用特殊的 ^ twigil。傳遞的值按字母順序分配給佔位符變數

for 1..3 {
  say $^a;    # 1
  say $^c;    # 3
  say $^b;    # 2
}
華夏公益教科書