跳转到内容

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
华夏公益教科书