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"
一旦我们定义了一个子例程,我们就可以在以后调用它来获取结果或操作。我们已经看到了内置的 `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