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
变量在块外不可见。作用域确保变量仅在需要时使用,并且在不应修改时不修改它们。块不需要与任何特定结构相关联,例如 if
或 loop
。块可以独立存在
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
}