Signatures 出现在 子例程方法 名称后面的括号中,出现在 -><-> 箭头后面的块中,作为 变量声明符(如 my)的输入,或作为以冒号开头的单独术语。

sub f($x{ }
#    ^^^^ Signature of sub f 
my method x() { }
#          ^^ Signature of a method 
my $s = sub (*@a{ }
#           ^^^^^ Signature of an anonymous function 
 
for <a b c> -> $x { }
#              ^^   Signature of a Block 
 
my ($a@b= 5, (678);
#  ^^^^^^^^ Signature of a variable declarator 
 
my $sig = :($a$b);
#          ^^^^^^^^ Standalone Signature object

签名字面量可用于定义回调或闭包的签名。

sub f(&c:(Int)) { }
sub will-work(Int{ }
sub won't-work(Str{ }
f(&will-work);
 
f(&won't-work);
CATCH { default { put .^name''.Str } };
# OUTPUT: «X::TypeCheck::Binding::Parameter: Constraint type check failed in binding to parameter '&c'␤» 
 
f(-> Int { 'this works too' } );

你可以在签名中使用任何类型的字面量,包括数字字面量;这通常与多值一起使用

proto stuff(|) {*}
multi stuff(33{ 58 }
multi stuff(⅓) { 43 }
multi stuff(Int)  { 3 }
multi stuff(Complex)  { 66 }
say stuff($_for (33, ⅓, i48); # OUTPUT: «58␤43␤66␤3␤» 

但是,你不能在签名中使用 TrueFalse 作为字面量,因为它们总是成功(或失败)。如果你这样做,将发出警告

sub foo(True{};
my $sig =  :True );

它们都会发出警告“签名中的字面量值与 True 进行智能匹配,并且智能匹配总是成功。请改用 where 子句”。使用 False 会产生类似的警告。

支持对 List 进行智能匹配签名。

my $sig = :(Int $iStr $s);
say (10'answer'~~ $sig;
# OUTPUT: «True␤» 
my $sub = sub ( Str $sInt $i ) { return $s xx $i };
say $sub.signature ~~ :StrInt );
# OUTPUT: «True␤» 
given $sig {
    when :(StrInt{ say 'mismatch' }
    when :($, $)     { say 'match' }
    default          { say 'no match' }
}
# OUTPUT: «match␤»

它匹配第二个 when 子句,因为 :($, $) 表示具有两个标量、匿名参数的 Signature,这是 $sig 的更通用版本。

当对 Hash 进行智能匹配时,签名被假定为由 Hash 的键组成。

my %h = left => 1right => 2;
say %h ~~ :(:$left:$right);
# OUTPUT: «True␤»

Signature 字面量可以包含字符串/数字字面量

my $sig = :('Þor'StrInt);
say <Þor Hammer 1> ~~ $sig# OUTPUT: «True␤»

它们还可以包含调用者标记

class Foo {
    method bar$self: ){ "baz" }
};
say Foo.^methods.first(*.name eq 'bar').signature ~~ :($: *%) ;
# OUTPUT: «True␤»

参数分隔符§

签名由零个或多个 Parameter 组成,这些参数由逗号分隔。

my $sig = :($a@b%c);
sub add($a$b{ $a + $b };

作为例外,第一个参数可以在逗号后跟一个冒号,而不是逗号,以标记方法的调用者。这样做是为了将其与常规位置参数区分开来。调用者是用于调用方法的对象,通常绑定到 self。通过在签名中指定它,您可以更改其绑定的变量名称。

method ($a: @b%c{};       # first argument is the invocant 
 
class Foo {
    method whoami($me:{
        "Well I'm class $me.^name(), of course!"
    }
}
say Foo.whoami# OUTPUT: «Well I'm class Foo, of course!␤»

类型约束§

参数可以选择具有类型约束(默认值为 Any)。这些约束可用于限制函数的允许输入。

my $sig = :(Int $aStr $b);

类型约束可以具有任何编译时定义的值

subset Positive-integer of Int where * > 0;
sub divisors(Positive-integer $n{ $_ if $n %% $_ for 1..$n };
CATCH { default { put .^name''.Str.resume } };
 
divisors 2.5;
# OUTPUT: «X::TypeCheck::Binding::Parameter: Type check failed in binding to parameter '$n'; expected Positive-integer but got Rat (2.5)␤» 
 
divisors -3;
# OUTPUT: «X::TypeCheck::Binding::Parameter: Constraint type check failed in binding to parameter '$n'; expected Positive-integer but got Int (-3)␤» 

请注意,在上面的代码中,类型约束在两个不同的级别上强制执行:第一级检查它是否属于子集所在的类型,在本例中为 Int。如果失败,则会产生 Type check 错误。一旦清除该过滤器,就会检查定义子集的约束,如果失败,则会产生 Constraint type check 错误。

类型约束可以定义多个允许的类型

sub abbrev($arg where Str|List|Hash{...} # throws if $arg is not one of those types 

如果您实际上不需要按名称引用参数,例如在 multi 中区分不同的签名或检查 Callable 的签名,那么匿名参数也可以。

my $sig = :($@%a);          # two anonymous and a "normal" parameter 
$sig = :(IntPositional);      # just a type is also fine (two parameters) 
sub baz(Str{ "Got passed a Str" }

类型约束也可以是 类型捕获

除了这些名义类型之外,还可以以代码块的形式对参数施加其他约束,这些代码块必须返回真值才能通过类型检查

sub f(Real $x where { $x > 0 }Real $y where { $y >= $x }{ }

where 子句中的代码有一些限制:任何产生副作用(例如,打印输出、从迭代器中提取或增加状态变量)的内容都不受支持,如果使用,可能会产生令人惊讶的结果。此外,where 子句的代码在某些实现中可能针对单个类型检查运行多次。

where 子句不必是代码块,where 子句右侧的任何内容都将用于 smartmatch 其参数。因此,您还可以编写

multi factorial(Int $ where 0{ 1 }
multi factorial(Int $x)        { $x * factorial($x - 1}

第一个可以缩短为

multi factorial(0{ 1 }

即,您可以直接使用文字作为匿名参数的类型和值约束。

提示:注意不要在您有几个条件时意外地省略一个块

-> $y where   .so && .name    {}sub one   {} ); # WRONG!! 
-> $y where { .so && .name }  {}sub two   {} ); # OK! 
-> $y where   .so &  .name.so {}sub three {} ); # Also good

第一个版本是错误的,并且会发出有关强制转换为字符串的子对象的警告。原因是表达式等效于 ($y ~~ ($y.so && $y.name));即“调用 .so,如果为 True,则调用 .name;如果也为 True,则将其值用于智能匹配……”。这是 (.so && .name)结果它将被智能匹配,但我们希望检查 .so.name 都是真值。这就是为什么显式块或 Junction 是正确版本的原因。

所有不属于 Signature 中子签名的先前参数都可以在跟随参数的 where 子句中访问。因此,最后一个参数的 where 子句可以访问不属于子签名的签名中的所有参数。对于子签名,将 where 子句放在子签名内。

sub foo($a$b where * == $a ** 2{ say "$b is a square of $a" }
foo 24# OUTPUT: «4 is a square of 2␤»» 
# foo 2, 3; 
# OUTPUT: «Constraint type check failed in binding to parameter '$b'…»

约束可选参数§

可选参数也可以有约束。任何参数上的where子句都将执行,即使它是可选的并且未由调用者提供。在这种情况下,您可能必须在where子句中防止未定义的值。

sub f(Int $aUInt $i? where { !$i.defined or $i > 5 }{ ... }

约束 slurpy 参数§

Slurpy 参数不能有类型约束。where子句结合Junction可以用于此目的。

sub f(*@a where {$_.all ~~ Int}{ say @a };
f(42);
f(<a>);
CATCH { default { say .^name' ==> '.Str }  }
# OUTPUT: «[42]␤Constraint type check failed in binding to parameter '@a' ...» 

约束命名参数§

命名参数的约束适用于冒号对的值部分。

sub f(Int :$i){};
f :i<forty-two>;
CATCH { default { say .^name' ==> '.Str }  }
# OUTPUT: «X::TypeCheck::Binding::Parameter ==> Type check failed in 
# binding to parameter '$i'; expected Int but got Str ("forty-two")␤»

约束参数明确性§

通常,类型约束仅检查参数的值是否为正确类型。至关重要的是,对象实例类型对象都将满足此类约束,如下所示

say  42.^name;    # OUTPUT: «Int␤» 
say  42 ~~ Int;   # OUTPUT: «True␤» 
say Int ~~ Int;   # OUTPUT: «True␤»

请注意42Int如何满足匹配。

有时我们需要区分这些对象实例(42)和类型对象(Int)。考虑以下代码

sub limit-lines(Str $sInt $limit{
    my @lines = $s.lines;
    @lines[0 .. min @lines.elems$limit].join("\n")
}
say (limit-lines "\n b \n c \n d \n"3).raku# "a \n b \n c \n d " 
say limit-lines Str3;
CATCH { default { put .^name''.Str } };
# OUTPUT: «X::Multi::NoMatch: Cannot resolve caller lines(Str: ); 
# none of these signatures match: 
#     (Str:D $: :$count!, *%_) 
#     (Str:D $: $limit, *%_) 
#     (Str:D $: *%_)» 
say limit-lines "\n b"Int# Always returns the max number of lines

在这里,我们实际上只想处理字符串实例,而不是类型对象。为此,我们可以使用:D类型约束。此约束检查传递的值是否为对象实例,类似于调用其DEFINITE(元)方法。

为了热身,让我们将:D应用于我们卑微的Int示例的右侧

say  42 ~~ Int:D;  # OUTPUT: «True␤» 
say Int ~~ Int:D;  # OUTPUT: «False␤»

请注意,在上面只有42匹配Int:D

返回limit-lines,我们现在可以修改其签名以尽早捕获错误

sub limit-lines(Str:D $sInt $limit{ };
say limit-lines Str3;
CATCH { default { put .^name ~ '--' ~ .Str } };
# OUTPUT: «Parameter '$s' of routine 'limit-lines' must be an object instance of type 'Str', 
#          not a type object of type 'Str'.  Did you forget a '.new'?»

这比程序以前失败的方式好得多,因为这里的失败原因更清楚。

类型对象也有可能是例程接受的唯一对象。这可以通过:U类型约束来完成,该约束检查传递的值是否为类型对象而不是对象实例。这是我们的Int示例,这次应用了:U

say  42 ~~ Int:U;  # OUTPUT: «False␤» 
say Int ~~ Int:U;  # OUTPUT: «True␤»

现在42无法匹配Int:U,而Int成功匹配。

这里有一个更实际的例子

sub can-turn-into(Str $stringAny:U $type{
   return so $string.$type;
}
say can-turn-into("3"Int);        # OUTPUT: «True␤» 
say can-turn-into("6.5"Int);      # OUTPUT: «True␤» 
say can-turn-into("6.5"Num);      # OUTPUT: «True␤» 
say can-turn-into("a string"Num); # OUTPUT: «False␤»

can-turn-into作为其第二个参数的对象实例调用将按预期产生约束违规

say can-turn-into("a string"123);
# OUTPUT: «Parameter '$type' of routine 'can-turn-into' must be a type object 
# of type 'Any', not an object instance of type 'Int'...» 

为了明确指示正常行为,即不约束参数是实例还是类型对象,可以使用:_,但这没有必要,因为这是参数的默认约束(此类)。因此,:(Num:_ $):(Num $)相同。

回顾一下,这里是对这些类型约束(也称为类型笑脸)的快速说明

# Checking a type object 
say Int ~~ Any:D;    # OUTPUT: «False␤» 
say Int ~~ Any:U;    # OUTPUT: «True␤» 
say Int ~~ Any:_;    # OUTPUT: «True␤» 
 
# Checking a subset 
subset Even of Int where * // 2;
say 3 ~~ Even:D;     # OUTPUT: «True␤» 
say 3 ~~ Even:U;     # OUTPUT: «False␤» 
say Int ~~ Even:U;   # OUTPUT: «True␤» 
 
# Checking an object instance 
say 42 ~~ Any:D;     # OUTPUT: «True␤» 
say 42 ~~ Any:U;     # OUTPUT: «False␤» 
say 42 ~~ Any:_;     # OUTPUT: «True␤» 
 
# Checking a user-supplied class 
class Foo {};
say Foo ~~ Any:D;    # OUTPUT: «False␤» 
say Foo ~~ Any:U;    # OUTPUT: «True␤» 
say Foo ~~ Any:_;    # OUTPUT: «True␤» 
 
# Checking an instance of a class 
my $f = Foo.new;
say $f  ~~ Any:D;    # OUTPUT: «True␤» 
say $f  ~~ Any:U;    # OUTPUT: «False␤» 
say $f  ~~ Any:_;    # OUTPUT: «True␤»

类和对象文档进一步阐述了实例和类型对象的概念,并使用.DEFINITE方法发现它们。

请记住,所有参数都有值;即使是可选参数也有默认值,这些默认值是显式类型约束的受约束类型的类型对象。如果不存在显式类型约束,则默认值对于方法、子方法和子例程是 Any 类型对象,对于块是 Mu 类型对象。这意味着,如果您使用 :D 类型笑脸,则需要提供默认值或使参数成为必需参数。否则,默认值将是类型对象,这将导致确定性约束失败。

sub divide (Int:D :$a = 2Int:D :$b!{ say $a/$b }
divide :1a, :2b; # OUTPUT: «0.5␤»

当特定参数(位置参数或命名参数)完全没有值时,将启用默认值。

sub f($a = 42){
  my $b is default('answer');
  say $a;
  $b = $a;
  say $b
};
f;     # OUTPUT: «42␤42␤» 
f Nil# OUTPUT: «Nil␤answer␤»

$a 的默认值为 42。如果没有值,$a 将被分配在 Signature 中声明的默认值。但是,在第二种情况下,它确实接收了一个值,恰好是 Nil。将 Nil 分配给任何变量都会将其重置为其默认值,该默认值已通过使用 default 特性声明为 'answer'。这解释了我们在第二次调用 f 时发生的情况。例程参数和变量以不同的方式处理默认值,这在一定程度上是由每种情况下声明默认值的不同方式(对于参数使用 =,对于变量使用 default 特性)所阐明的。

注意:在 6.c 语言中,:U/:D 受约束变量的默认值是具有此类约束的类型对象,该对象不可初始化,因此您不能使用 .= 运算符,例如。

use v6.c;
my Int:D $x .= new: 42;
# OUTPUT: You cannot create an instance of this type (Int:D) 
# in block <unit> at -e line 1 

在 6.d 语言中,默认的 default 是没有笑脸约束的类型对象

use v6.d;
my Int:D $x .= new: 42# OUTPUT: «42␤» 

关于术语的结束语:本节讨论了使用类型笑脸 :D:U 来约束参数的确定性。有时,定义性被用作确定性的同义词;这可能会令人困惑,因为这些术语具有细微的差别。

如上所述,确定性关注类型对象和对象实例之间的区别。类型对象始终是不确定的,而对象实例始终是确定的。可以使用 DEFINITE(元)方法验证对象是类型对象/不确定还是对象实例/确定。

确定性应与定义性区分开来,后者关注已定义对象和未定义对象之间的差异。可以使用 defined 方法验证对象是已定义还是未定义,该方法在 Mu 类中实现。默认情况下,类型对象被视为未定义,而对象实例被视为已定义;也就是说:.defined 在类型对象上返回 False,否则返回 True。但是,此默认行为可以被子类覆盖。覆盖默认 .defined 行为的子类的示例是 Failure,因此,即使是实例化的 Failure 也充当未定义值

my $a = Failure;                # Initialize with type object 
my $b = Failure.new("foo");     # Initialize with object instance 
say $a.DEFINITE;                # OUTPUT: «False␤» : indefinite type object 
say $b.DEFINITE;                # OUTPUT: «True␤»  : definite object instance 
say $a.defined;                 # OUTPUT: «False␤» : default response 
say $b.defined;                 # OUTPUT: «False␤» : .defined override

约束 Callable 的签名§

:u

允许空格)

sub apply(&l:(Int:D --> Int:D), Int:D \n{
    l(n)
}
 
sub identity(Int:D \i --> Int:D{ i }
sub double(Int:D \x --> Int:D{ 2 * x }
 
say apply &identity10# OUTPUT: «10␤» 
say apply &double10;   # OUTPUT: «20␤»

带类型的 lambda 也适用于受约束的可调用参数。

say apply -> Int:D \x --> Int:D { 2 * x }3;  # OUTPUT: «6␤» 
say apply -> Int:D \x --> Int:D { x ** 3 }3# OUTPUT: «27␤» 

请注意,此简写语法仅适用于带有 & 标记的参数。对于其他参数,您需要使用长版本

sub play-with-tens($c where .signature ~~ :(IntStr)) { say $c(10'ten'}
sub by-joining-them(Int $iStr $s{ $s ~ $i }
play-with-tens &by-joining-them;                              # OUTPUT: «ten10␤» 
play-with-tens -> Int \iStr \s { s x (1..10).roll mod i };  # OUTPUT: «tenten␤» 
 
sub g(Num $iStr $s{ $s ~ $i }
# play-with-tens(&g); # Constraint type check failed

约束返回类型§

有多种方法可在 Routine 上约束返回类型。以下所有版本当前均有效,并且将在成功执行例程时强制执行类型检查。

NilFailure 始终允许作为返回类型,无论任何类型约束如何。这允许 Failure 返回并向下传递调用链。

sub foo(--> Int{ Nil };
say foo.raku# OUTPUT: «Nil␤»

不支持类型捕获。

返回类型箭头:-->§

首选在签名中指示返回类型(或常量)的这种形式,因为它可以处理常量值,而其他形式则不能。为了保持一致性,这是本网站上唯一接受的形式。

返回类型箭头必须放在参数列表的末尾,前面可以带或不带 ,

sub greeting1(Str $name  --> Str{ say "Hello, $name" } # Valid 
sub greeting2(Str $name--> Str{ say "Hello, $name" } # Valid 
 
sub favorite-number1(--> 42{        } # OUTPUT: 42 
sub favorite-number2(--> 42{ return } # OUTPUT: 42 

如果类型约束是常量表达式,则将其用作例程的返回值。该例程中的任何返回语句都必须没有参数。

sub foo(Str $word --> 123{ say $wordreturn}
my $value = foo("hello"); # OUTPUT: hello 
say $value;               # OUTPUT: 123 
# The code below will not compile 
sub foo(Str $word --> 123{ say $wordreturn $word}
my $value = foo("hello");
say $value;

returns§

签名声明后面的关键字 returns--> 具有相同的功能,但需要注意的是,此形式不适用于常量值。你也不能在块中使用它。这就是始终首选尖角箭头形式的原因。

sub greeting(Str $namereturns Str { say "Hello, $name" } # Valid 
sub favorite-number returns 42 {        } # This will fail. 

of§

of 只是 returns 关键字的真实名称。

sub foo() of Int { 42 }# Valid 
sub foo() of 42 {  };    # This will fail. 

前缀(类似 C)形式§

这类似于对变量施加类型约束,例如 my Type $var = 20;,除了 $var 是例程的定义。

my Int sub bar { 1 };     # Valid 
my 42 sub bad-answer {};  # This will fail. 

强制转换类型§

要接受一种类型但自动将其强制转换为另一种类型,请将接受的类型用作目标类型的参数。如果接受的类型是 Any,则可以省略它。

sub f(Int(Str$want-intStr() $want-str{
    say $want-int.^name ~ ' ' ~ $want-str.^name
}
f '10'10;
# OUTPUT: «Int Str␤» 
 
sub foo(Date(Str$d{ say $d.^namesay $d };
foo "2016-12-01";
# OUTPUT: «Date␤2016-12-01␤»

如果存在,则通过调用具有要强制转换到的类型名称的方法来执行强制转换。在此示例中,我们在 Str 类上调用内置方法 Date。该方法假定返回正确的类型——当前不会对结果执行其他检查。

强制转换也可以在返回类型上执行

sub square-str (Int $x --> Str(Int)) {
    $x²
}
 
for 2,4*²  … 256 -> $a {
    say $a"² is "square-str$a ).chars" figures long";
}
 
# OUTPUT: «2² is 1 figures long␤ 
#          4² is 2 figures long␤ 
#          16² is 3 figures long␤ 
#          256² is 5 figures long␤» 

在此示例中,将返回类型强制转换为 Str 允许我们直接应用字符串方法,例如字符数。

注意:参数上必须有适当的方法,因此在尝试强制转换自定义类型时要小心。

class Foo { }
 
sub bar(Foo(Int$x{ say $x }
 
bar(3);
# OUTPUT: «Impossible coercion from 'Int' into 'Foo': no acceptable coercion method found␤» 

Slurpy 参数§

如果一个函数可以接受数量可变的参数,则称其为可变参数,即其元数不是固定的。因此,可选、命名和 slurpy 参数使得使用它们的例程成为可变参数,并由此称为可变参数。这里我们将重点介绍 slurpy 参数,或简单地称为slurpies

数组或哈希参数可以通过前导单个星号 (*) 或双星号 (**) 或前导加号 (+) 标记为slurpy。slurpy 参数可以绑定到任意数量的参数(零个或更多),并且它将导致与符号兼容的类型。

之所以称为“slurpy”,是因为它们像有人在 slurping 面条一样吸收了函数的任何剩余参数。

my $sig1 = :($a@b);  # exactly two arguments, second must be Positional 
my $sig2 = :($a*@b); # at least one argument, @b slurps up any beyond that 
my $sig3 = :(*%h);     # no positional arguments, but any number 
                       # of named arguments 
 
sub one-arg (@)  { }
sub slurpy  (*@) { }
one-arg (567); # ok, same as one-arg((5, 6, 7)) 
slurpy  (567); # ok 
slurpy   567 ; # ok 
# one-arg(5, 6, 7) ; # X::TypeCheck::Argument 
# one-arg  5, 6, 7 ; # X::TypeCheck::Argument 
 
sub named-names (*%named-args{ %named-args.keys };
say named-names :foo(42:bar<baz># OUTPUT: «foo bar␤» 

位置和命名 slurpies 可以组合;命名参数(即,Pair)收集在指定的哈希中,位置参数收集在数组中

sub combined-slurpy (*@a*%h{ { array => @ahash => %h } }
# or: sub combined-slurpy (*%h, *@a) { ... } 
 
say combined-slurpy(one => 1two => 2);
# OUTPUT: «{array => [], hash => {one => 1, two => 2}}␤» 
say combined-slurpy(one => 1two => 234);
# OUTPUT: «{array => [3 4], hash => {one => 1, two => 2}}␤» 
say combined-slurpy(one => 1two => 234five => 5);
# OUTPUT: «{array => [3 4], hash => {five => 5, one => 1, two => 2}}␤» 
say combined-slurpy(one => 1two => 234five => 56);
# OUTPUT: «{array => [3 4 6], hash => {five => 5, one => 1, two => 2}}␤» 

请注意,在 slurpy(或事实上在任何类型的可变参数)参数之后不允许出现位置参数

:(*@args$last);
# ===SORRY!=== Error while compiling: 
# Cannot put required parameter $last after variadic parameters 

通常,slurpy 参数将创建一个Array(或兼容类型),为每个参数创建一个新的Scalar容器,并将每个参数的值分配给这些Scalar。如果原始参数也有一个中间Scalar,则在此过程中将绕过它,并且在被调用的函数中不可用。

Sigiled 参数始终会对收集的参数施加上下文。无 Sigil 参数也可以使用 slurpily,前面加上一个 + 号,以使用它们最初开始的任何初始类型

sub zipi+zape ) {
    zape.^name => zape
};
say zipi"Hey "); # OUTPUT: «List => (Hey )␤» 
say zipi1...* ); # OUTPUT: «Seq => (...)␤» 

当与一些特征和修饰符结合时,slurpy 参数具有特殊行为,如slurpy 数组参数部分中所述。

如果方法没有声明另一个 slurpy 命名参数,则它们会自动获得一个*%_ slurpy 命名参数。

slurpy 数组参数的类型§

slurpy 数组参数有三种变体。

  • 单星号形式展平传递的参数。

  • 双星号形式不展平参数。

  • 加号形式根据单个参数规则展平。

将在接下来的几节中详细描述每个参数。由于每个参数之间的差异有点细微,因此提供了每个参数的示例,以演示每个 slurpy 约定如何与其他约定不同。

展平 slurpy§

使用一个星号声明的 slurpy 参数将通过溶解一层或多层裸Iterable来展平参数。

my @array = <a b c>;
my $list := <d e f>;
sub a(*@a)  { @a.raku.say };
a(@array);                 # OUTPUT: «["a", "b", "c"]␤» 
a(1$list, [23]);       # OUTPUT: «[1, "d", "e", "f", 2, 3]␤» 
a([12]);                 # OUTPUT: «[1, 2]␤» 
a(1, [12], ([34], 5)); # OUTPUT: «[1, 1, 2, 3, 4, 5]␤» 
a(($_ for 123));       # OUTPUT: «[1, 2, 3]␤» 

单个星号 slurpy 展平所有给定的可迭代对象,有效地将使用逗号创建的任何对象提升到顶层。

未展平 slurpy§

使用两个星号声明的 slurpy 参数不会展平列表中的任何Iterable参数,而是或多或少地保持参数原样

my @array = <a b c>;
my $list := <d e f>;
sub b(**@b{ @b.raku.say };
b(@array);                 # OUTPUT: «[["a", "b", "c"],]␤» 
b(1$list, [23]);       # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]␤» 
b([12]);                 # OUTPUT: «[[1, 2],]␤» 
b(1, [12], ([34], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]␤» 
b(($_ for 123));       # OUTPUT: «[(1, 2, 3),]␤» 

双星号 slurpy 隐藏嵌套的逗号对象,并按原样保留在 slurpy 数组中。

单参数规则 slurpy§

使用加号创建的 slurpy 参数会启用“单参数规则”,该规则会根据上下文决定如何处理 slurpy 参数。基本上,如果只传递了一个参数,并且该参数是 Iterable,则该参数将用于填充 slurpy 参数数组。在任何其他情况下,+@ 的工作方式与 **@ 相同。

my @array = <a b c>;
my $list := <d e f>;
sub c(+@b{ @b.raku.say };
c(@array);                 # OUTPUT: «["a", "b", "c"]␤» 
c(1$list, [23]);       # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]␤» 
c([12]);                 # OUTPUT: «[1, 2]␤» 
c(1, [12], ([34], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]␤» 
c(($_ for 123));       # OUTPUT: «[1, 2, 3]␤» 

有关其他讨论和示例,请参阅 函数的 slurpy 约定

类型捕获§

类型捕获允许将类型约束的规范推迟到调用函数时。它们允许在签名和函数体中同时引用类型。

sub f(::T $p1T $p2, ::C){
    # $p1 and $p2 are of the same type T, that we don't know yet 
    # C will hold a type we derive from a type object or value 
    my C $division = $p1 / $p2;
    return sub (T $p1{
        $division * $p1;
    }
}
 
# The first parameter is Int and so must be the 2nd. 
# We derive the 3rd type from calling the operator that is used in &f. 
my &s = f(102Int.new / Int.new);
say s(2)# 10 / 2 * 2 == 10

位置参数与命名参数§

参数可以是位置参数命名参数。默认情况下,参数是位置参数,但 slurpy 哈希和以冒号 : 开头的参数除外。后者称为 冒号对。检查以下签名及其表示的含义

$sig1 = :($a);               # a positional argument 
$sig2 = :(:$a);              # a named argument of name 'a' 
$sig3 = :(*@a);              # a slurpy positional argument 
$sig4 = :(*%h);              # a slurpy named argument 

在调用者方面,位置参数按声明参数的顺序传递。

sub pos($x$y{ "x=$x y=$y" }
pos(45);                          # OUTPUT: «x=4 y=5»

对于命名参数和参数,仅使用名称将参数映射到参数。如果使用粗箭头构造 Pair,则只有那些具有有效标识符作为键的参数才被识别为命名参数。

sub named(:$x:$y{ "x=$x y=$y" }
named=> 5x => 4);             # OUTPUT: «x=4 y=5» 

您可以使用与命名参数同名的变量调用例程;在这种情况下,: 将用于调用,以便将变量的名称理解为参数的键。

sub named-shortcut:$shortcut ) {
    say "Looks like $shortcut"
}
named-shortcutshortcut => "to here"); # OUTPUT: «Looks like to here␤» 
my $shortcut = "Þor is mighty";
named-shortcut:$shortcut );           # OUTPUT: «Looks like Þor is mighty␤»

命名参数的名称可以与变量名称不同

sub named(:official($private)) { "Official business!" if $private }
named :official;

参数别名§

冒号对 语法可用于为参数提供别名

sub alias-named(:color(:$colour), :type(:class($kind))) {
    say $colour ~ " " ~ $kind
}
alias-named(color => "red"type => "A");    # both names can be used 
alias-named(colour => "green"type => "B"); # more than two names are ok 
alias-named(color => "white"class => "C"); # every alias is independent

冒号 : 的存在将决定我们是否创建一个新的命名参数。:$colour 不仅将成为别名变量的名称,还将成为一个新的命名参数(在第二次调用中使用)。但是,$kind 将只是别名变量的名称,不会创建一个新的命名参数。可以在 sub MAIN 中找到更多别名的用法。

具有命名参数的函数可以动态调用,对 Pair 进行解引用,使用 | 将其转换为命名参数。

multi f(:$named{ note &?ROUTINE.signature };
multi f(:$also-named{ note &?ROUTINE.signature };
for 'named''also-named' -> $n {
    f(|($n => rand))                # OUTPUT: «(:$named)␤(:$also-named)␤» 
}
 
my $pair = :named(1);
f |$pair;                           # OUTPUT: «(:$named)␤»

同样可以将 Hash 转换为命名参数。

sub f(:$also-named{ note &?ROUTINE.signature };
my %pairs = also-named => 4;
f |%pairs;                              # OUTPUT: «(:$also-named)␤»

当将包含列表的 Hash 滑入命名参数时,可能会出现问题。为避免额外的容器层,请在滑入之前强制转换为 Map

class C { has $.xhas $.yhas @.z };
my %h = <x y z> Z=> (520, [1,2]);
say C.new(|%h.Map);
# OUTPUT: «C.new(x => 5, y => 20, z => [1, 2])␤»

您可以为命名参数创建任意多个别名

sub alias-named(:color(:$colour),
                :variety(:style(:sort(:type(:class($kind)))))) {
    return $colour ~ " " ~ $kind
}
say alias-named(color => "red"style => "A");
say alias-named(colour => "green"variety => "B");
say alias-named(color => "white"class => "C");

您可以通过使参数成为 匿名参数 的别名来创建不会创建任何变量的命名参数。这在将命名参数仅用作选择 multi 候选者的方式时很有用,例如,通常在特性中使用

# Timestamps calls to a routine. 
multi trait_mod:<is>(Routine:D $r is raw:timestamped($)!{
    $r does my role timestamped { has Instant:D @.timestamps };
    $r.wrap: -> | { ENTER $r.timestamps.push: nowcallsame };
}
 
sub foo is timestamped { }
foo;
say +&foo.?timestamps# OUTPUT: «1␤» 

可选参数和必需参数§

默认情况下,位置参数是必需的,可以通过默认值或尾随问号使其变为可选的

$sig1 = :(Str $id);         # required parameter 
$sig2 = :($base = 10);      # optional parameter, default value 10 
$sig3 = :(Int $x?);         # optional parameter, default is the Int type object 

默认情况下,命名参数是可选的,可以通过尾随感叹号使其变为强制性的

$sig1 = :(:%config);        # optional parameter 
$sig2 = :(:$debug = False); # optional parameter, defaults to False 
$sig3 = :(:$name!);         # mandatory 'name' named parameter 

默认值可以依赖于先前的参数,并且(至少在概念上)为每个调用重新计算

$sig1 = :($goal$accuracy = $goal / 100);
$sig2 = :(:$excludes = ['.''..']);        # a new Array for every call 

是否为参数传递了参数?§

显示是否为给定参数传递参数的检查的表格

参数类型示例注释检查是否未传递参数
Slurpy*@array不要使用 .defined 进行检查if not @array
必需$foo不能省略(不适用)
可选@bar = default选择一个合适的默认值¹if @bar =:= default

¹ 合适的默认值是一个具有不同标识的对象,可以通过 WHICH 方法进行检查。

具有默认值的参数始终是可选的,因此无需使用 ?is optional 特征对其进行标记。

然后,您可以在例程的主体中使用 =:= 容器标识运算符 来检查是否将此确切的默认值绑定到参数。

带有位置参数的示例

my constant PositionalAt = Positional.new;
 
sub a (@list = PositionalAt{ say @list =:= PositionalAt }
a;              # OUTPUT: «True␤» 
a [123];    # OUTPUT: «False␤» 

带有某些标量参数的示例

my constant AnyAt = Any.new;
 
sub b ($x=AnyAt:$y=AnyAt{ say $x =:= AnyAtsay $y =:= AnyAt }
b 1;            # OUTPUT: «False␤True␤» 
b 1:2y;       # OUTPUT: «False␤False␤» 

如果您的参数已输入,则 类型笑脸 可与 multi 一起使用,如下所示

multi c (Int:U $z)  { say 'Undefined' }
multi c (Int:D $z)  { say 'Defined'   }
multi c (Int   $z?{ say 'Omitted'   }
c;              #Omitted 
c (Int);        #Undefined 
c 42;           #Defined 

这些示例使用诸如 PositionalAt 之类的名称来反映 .WHICH 测试返回类型为 ObjAt 的对象,您可以自由地创建自己的名称。

动态变量§

动态变量 允许在签名中使用,尽管它们不提供特殊行为,因为参数绑定无论如何都会连接两个范围。

解构参数§

非标量参数可以后跟或替换括号中的子签名,这将解构给定的参数。列表的解构只是它的元素

sub first(@array ($first*@rest)) { $first }

sub first([$f*@]) { $f }

而散列的解构是它的对

sub all-dimensions(% (:length(:$x), :width(:$y), :depth(:$z))) {
    $x andthen $y andthen $z andthen True
}

尖角循环还可以解构散列,允许赋值给变量

my %hhgttu = (:40life, :41universe, :42everything);
for %hhgttu -> (:$key:$value{
  say "$key → $value";
}
# OUTPUT: «universe → 41␤life → 40␤everything → 42␤»

通常,对象根据其属性进行解构。一个常见的惯用语是在 for 循环中解包 Pair 的键和值

for <Peter Paul Merry>.pairs -> (:key($index), :value($guest)) { }

但是,将对象解包为其属性只是默认行为。要使对象以不同的方式进行解构,请更改其 Capture 方法。

子签名§

要匹配复合参数,请使用括号中参数名称后面的子签名。

sub foo(|c(IntStr)){
   put "called with {c.raku}"
};
foo(42"answer");
# OUTPUT: «called with \(42, "answer")␤»

长名称§

要排除某些参数在多重分派中被考虑,请用双分号将它们分开。

multi f(Int $iStr $s;; :$b{ say "$i$s{$b.raku}" };
f(10'answer');
# OUTPUT: «10, answer, Any␤»

捕获参数§

用竖线 | 给参数加前缀,会使该参数成为一个 Capture,用尽所有剩余的位置和命名参数。

这通常用于 proto 定义(如 proto foo (|) {*}),表示例程的 multi 定义 可以具有任何 类型约束。请参阅 proto 以获取示例。

如果绑定到变量,可以使用滑移运算符 | 将参数作为一个整体转发。

sub a(Int $iStr $s{ say $i.^name ~ ' ' ~ $s.^name }
sub b(|c{ say c.^namea(|c}
b(42"answer");
# OUTPUT: «Capture␤Int Str␤»

参数特征和修饰符§

默认情况下,参数绑定到其参数并标记为只读。可以使用参数上的特征来更改它。

is copy 特征导致复制参数,并允许在例程内对其进行修改

sub count-up($x is copy{
    $x = ∞ if $x ~~ Whatever;
    .say for 1..$x;
}

is rw 特征代表 is read-write,使参数绑定到变量(或其他可写容器)。对参数赋值会更改调用方侧变量的值。

sub swap($x is rw$y is rw{
    ($x$y= ($y$x);
}

对于 slurpy 参数,is rw 被保留供语言设计者将来使用。

is raw 特征 会自动应用于使用 反斜杠加号 作为“sigil”声明的参数,也可以用来使通常使用 sigil 的参数表现得像这些参数一样。在 slurpy 的特殊情况下,它通常会生成一个由 Scalar 填充的 Array,如上所述,is raw 反而会使参数生成一个 List。该列表的每个元素都将直接绑定为原始参数。

要明确要求只读参数,请使用 is readonly 特征。请注意,这仅适用于容器。内部对象很可能具有变异器方法,并且 Raku 不会对对象的属性强制实施不可变性。

特征后面可以跟 where 子句

sub ip-expand-ipv6($ip is copy where m:i/^<[a..f\d\:]>**3..39$/{ }