跳转至内容

Raku 编程/控制结构

来自 Wikibooks,开放世界中的开放书籍

流控制

[编辑 | 编辑源代码]

我们在前面的章节中已经了解了如何创建变量并使用它们来执行基本的算术运算和其他操作。现在我们将介绍流控制的概念,使用特殊的结构进行分支循环

块是 { } 花括号内的代码块。这些代码块以多种方式与周围代码分离。它们还为变量定义了一个新的作用域:用 my 定义并在块内使用的变量在块外不可见或不可用。这使得代码能够被分隔,以确保仅用于临时使用的变量只在临时使用。

分支可以使用两种语句之一进行:ifunlessif 可选择地还具有 else 子句。if 语句评估给定的条件,如果它是真语句,则执行 if 后面的块。当语句为假时,如果有,则改为执行 else 块。unless 语句则相反。它评估一个条件,只有在条件为假时才执行其块。使用 unless 时不能使用 else 子句。

关系运算符

[编辑 | 编辑源代码]

有许多关系运算符可用于确定真值。以下是一些运算符

$x == $y;                  # $x and $y are equal
$x > $y;                   # $x is greater than $y
$x >= $y;                  # $x is greater than or equal to $y
$x < $y;                   # $x is less than $y
$x <= $y;                  # $x is less than or equal to $y
$x != $y;                  # $x is not equal to $y

所有这些运算符都返回布尔值,并且可以分配给变量

$x = (5 > 3);              # $x is True
$y = (5 == 3);             # $y is False

上面的括号仅用于清晰度;它们实际上不是必需的。

if/unless

[编辑 | 编辑源代码]

让我们从一个例子开始

my Int $x = 5;
if ($x > 3) {
     say '$x is greater than 3';         # This prints
}
else {
     say '$x is not greater than 3';     # This doesn't
}

请注意,在上面的示例中,if($x > 3) 之间有一个空格。这一点很重要,不是可选的。Raku 的解析规则在这方面很明确:任何以 ( 开头的括号结尾的词都被视为子例程调用。空格将此语句与子例程调用区分开来,并让解析器知道这是一个条件。

if($x > 5) {   # Calls subroutine "if"
}

if ($x > 5) {  # An if conditional
}

为了避免所有混淆,可以安全地省略括号。

if $x > 5 {     # Always a condition
}

unlessif 的行为相反

my Int $x = 5;
unless $x > 3 {
     say '$x is not greater than 3';         # This doesn't print
}

unless 后面不允许使用 else 子句。

后缀语法

[编辑 | 编辑源代码]

ifunless 不仅对标记要条件执行的块有用。它们还可以以自然的方式应用在语句的末尾,以仅影响该语句

$x = 5 if $y == 3;
$z++ unless $x + $y > 8;

上面的两行代码仅在满足其条件时才执行。第一行将 $x 设置为 5,如果 $y 等于 3。第二行递增 $z,除非 $x + $y 的总和大于 8。

智能匹配

[编辑 | 编辑源代码]

有时您想检查两件事是否匹配。关系运算符 == 检查两个值是否相等,但这非常有限。如果我们想检查其他相等关系怎么办?我们想要一个无论是什么都做我们想做的事情的运算符。这个神奇的运算符就是智能匹配运算符 ~~

现在,当您看到 ~~ 运算符时,您可能马上想到字符串。智能匹配运算符对字符串有很多用途,但并不局限于字符串。

以下是一些智能匹配运算符在实际中的例子

5 ~~ "5";                          # true, same numerical value
["a", "b"] ~~ *, "a", *;           # true, "a" contained in the array
("a" => 1, "b" => 2) ~~ *, "b", *; # true, hash contains a "b" key
"c" ~~ /c/;                        # true, "c" matches the regex /c/
3 ~~ Int                           # true, 3 is an Int

正如您所见,智能匹配运算符可以以多种方式使用来测试两件事,以查看它们是否以某种方式匹配。上面我们看到了正则表达式的示例,我们将在后面的章节中详细讨论。这也不是可以匹配事物的完整列表,我们将在整本书中看到更多内容。

Given / When

[编辑 | 编辑源代码]

Raku 具有将数量与多个不同备选方案进行匹配的功能。该结构是 givenwhen 块。

given $x {
  when Bool { say '$x is the boolean quantity ' ~ $x; }
  when Int { when 5 { say '$x is the number 5'; } }
  when "abc" { say '$x is the string "abc"'; }
}

每个 when 都是一个智能匹配。上面的代码等效于以下代码

if $x ~~ 5 {
  say '$x is the number 5';
} 
elsif $x ~~ "abc" {
  say '$x is the string "abc"';
}
elsif $x ~~ Bool {
  say '$x is the boolean quantity ' ~$x;
}

given/when 结构比 if/else 更简洁,并且在内部,它可能以更优化的方式实现。

循环是重复执行某些语句组多次的方法。Raku 有多种可用的循环类型,每种类型都有不同的用途。

for 循环

[编辑 | 编辑源代码]

for 块接受数组或范围作为参数,并遍历每个元素。在最基本的情况下,for 将每个连续的值分配给默认变量 $_。或者,可以列出特定变量来接收值。以下是一些 for 块的例子

# Prints the numbers "12345"
for 1..5 {          # Assign each value to $_
    .print;           # print $_;
}

# Same thing, but using an array
my @nums = 1..5;
for @nums {
    .print;
}

# Same, but uses an array that's not a range
my @nums = (1, 2, 3, 4, 5);
for @nums {
    .print;
}

# Using a different variable than $_
for 1..5 -> $var {
    print $var;
}

在上面的所有示例中,for 的数组参数也可以可选地用括号括起来。特殊的“尖”语法 -> 将在后面的章节中详细解释,但值得注意的是,我们可以将其扩展为在每次循环迭代中从数组中读取多个值

my @nums = 0..5;
for @nums -> $even, $odd {
  say "Even: $even Odd: $odd";
}

这将打印以下行

Even: 0 Odd: 1
Even: 2 Odd: 3
Even: 4 Odd: 5

for 也可以用作语句后缀,就像我们对 ifunless 所做的那样,尽管有一些注意事项

print $_ for (1..5);    # Prints "12345"
print for (1..5);        # Parse Error! Print requires an argument
.print for 1..5;  # Prints "12345"

C 语言程序员将认识到 loop 结构的行为,它与 C 语言中的 for 循环具有相同的格式和行为。Raku 重新使用了 for 的名称来表示上一节中看到的数组循环结构,并使用 loop 的名称来描述 C 语言循环的增量行为。以下是 loop 结构

loop (my $i = 0; $i <= 5; $i++) {
    print $i;            # "12345"
}

通常,loop 接受这三个组件

loop ( INITIALIZER ; CONDITION ; INCREMENTER )

loop 中的 INITIALIZER 是一行代码,它在循环开始之前执行,但具有与循环体相同的作用域。CONDITION 是在每次迭代之前检查的布尔测试。如果测试为假,则循环退出,如果为真,则循环重复。INCREMENTER 是在循环结束时发生的一条语句,在下一轮迭代开始之前。所有这些部分都可以选择性地省略。以下列出了五种编写相同循环的方法

loop (my $i = 0; $i <= 5; $i++) {
    print $i;            # "12345"
}

my $i = 0;    # Small Difference: $i is scoped differently
loop ( ; $i <= 5; $i++) {
    print $i;
}

loop (my $i = 0; $i <= 5; ) {
    print $i;            # "12345"
    $i++;
}

loop (my $i = 0; ; $i++) {
    last unless ($i <= 5);
    print $i;            # "12345"
}

my $i = 0;
loop ( ; ; ) {
    last unless ($i <= 5);
    print $i;            # "12345"
    $i++;
}

如果您想要一个无限循环,也可以省略括号,而不是使用 (;;)

my $i = 0;
loop {   # Possibly infinite loop
    last unless ($i <= 5);
    print $i;            # "12345"
    $i++;
}

repeat

[编辑 | 编辑源代码]

repeat 块将至少执行一次其主体,因为条件在块之后。在下面的示例中,您可以看到,即使$i大于2,该块仍然会运行。

my $i = 3;
repeat {
    say $i;
} while $i < 2;
华夏公益教科书