Signatures
出现在 子例程 和 方法 名称后面的括号中,出现在 ->
或 <->
箭头后面的块中,作为 变量声明符(如 my
)的输入,或作为以冒号开头的单独术语。
sub f()# ^^^^ Signature of sub fmy method x()# ^^ Signature of a methodmy = sub (*)# ^^^^^ Signature of an anonymous functionfor <a b c> -># ^^ Signature of a Blockmy (, ) = 5, (6, 7, 8);# ^^^^^^^^ Signature of a variable declaratormy = :(, );# ^^^^^^^^ Standalone Signature object
签名字面量可用于定义回调或闭包的签名。
sub f(:(Int))sub will-work(Int)sub won't-work(Str)f();f();CATCH ;# OUTPUT: «X::TypeCheck::Binding::Parameter: Constraint type check failed in binding to parameter '&c'»f(-> Int );
你可以在签名中使用任何类型的字面量,包括数字字面量;这通常与多值一起使用
proto stuff(|)multi stuff(33)multi stuff(⅓)multi stuff(Int)multi stuff(Complex)say stuff() for (33, ⅓, i, 48); # OUTPUT: «5843663»
但是,你不能在签名中使用 True
或 False
作为字面量,因为它们总是成功(或失败)。如果你这样做,将发出警告
sub foo(True) ;my = :( True );
它们都会发出警告“签名中的字面量值与 True
进行智能匹配,并且智能匹配总是成功。请改用 where
子句”。使用 False
会产生类似的警告。
支持对 List
进行智能匹配签名。
my = :(Int , Str );say (10, 'answer') ~~ ;# OUTPUT: «True»my = sub ( Str , Int ) ;say .signature ~~ :( Str, Int );# OUTPUT: «True»given# OUTPUT: «match»
它匹配第二个 when
子句,因为 :($, $)
表示具有两个标量、匿名参数的 Signature
,这是 $sig
的更通用版本。
当对 Hash
进行智能匹配时,签名被假定为由 Hash
的键组成。
my = left => 1, right => 2;say ~~ :(:, :);# OUTPUT: «True»
Signature
字面量可以包含字符串/数字字面量
my = :('Þor', Str, Int);say <Þor Hammer 1> ~~ ; # OUTPUT: «True»
它们还可以包含调用者标记
;say Foo.^methods.first(*.name eq 'bar').signature ~~ :($: *%) ;# OUTPUT: «True»
参数分隔符§
签名由零个或多个 Parameter
组成,这些参数由逗号分隔。
my = :(, , );sub add(, ) ;
作为例外,第一个参数可以在逗号后跟一个冒号,而不是逗号,以标记方法的调用者。这样做是为了将其与常规位置参数区分开来。调用者是用于调用方法的对象,通常绑定到 self
。通过在签名中指定它,您可以更改其绑定的变量名称。
method (: , ) ; # first argument is the invocantsay Foo.whoami; # OUTPUT: «Well I'm class Foo, of course!»
类型约束§
参数可以选择具有类型约束(默认值为 Any
)。这些约束可用于限制函数的允许输入。
my = :(Int , Str );
类型约束可以具有任何编译时定义的值
of Int where * > 0;sub divisors(Positive-integer ) ;CATCH ;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( where Str|List|Hash) # throws if $arg is not one of those types
如果您实际上不需要按名称引用参数,例如在 multi 中区分不同的签名或检查 Callable
的签名,那么匿名参数也可以。
my = :($, @, ); # two anonymous and a "normal" parameter= :(Int, Positional); # just a type is also fine (two parameters)sub baz(Str)
类型约束也可以是 类型捕获。
除了这些名义类型之外,还可以以代码块的形式对参数施加其他约束,这些代码块必须返回真值才能通过类型检查
sub f(Real where , Real where )
where
子句中的代码有一些限制:任何产生副作用(例如,打印输出、从迭代器中提取或增加状态变量)的内容都不受支持,如果使用,可能会产生令人惊讶的结果。此外,where
子句的代码在某些实现中可能针对单个类型检查运行多次。
where
子句不必是代码块,where
子句右侧的任何内容都将用于 smartmatch 其参数。因此,您还可以编写
multi factorial(Int $ where 0)multi factorial(Int )
第一个可以缩短为
multi factorial(0)
即,您可以直接使用文字作为匿名参数的类型和值约束。
提示:注意不要在您有几个条件时意外地省略一个块
-> where .so && .name ( sub one ); # WRONG!!-> where ( sub two ); # OK!-> 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(, where * == ** 2)foo 2, 4; # 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 , UInt ? where )
约束 slurpy 参数§
Slurpy 参数不能有类型约束。where
子句结合Junction
可以用于此目的。
sub f(* where ) ;f(42);f(<a>);CATCH# OUTPUT: «[42]Constraint type check failed in binding to parameter '@a' ...»
约束命名参数§
sub f(Int :);f :i<forty-two>;CATCH# 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»
请注意42
和Int
如何满足匹配。
有时我们需要区分这些对象实例(42
)和类型对象(Int
)。考虑以下代码
sub limit-lines(Str , Int )say (limit-lines "a \n b \n c \n d \n", 3).raku; # "a \n b \n c \n d "say limit-lines Str, 3;CATCH ;# 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 "a \n b", Int; # Always returns the max number of lines
在这里,我们实际上只想处理字符串实例,而不是类型对象。为此,我们可以使用:D
类型约束。此约束检查传递的值是否为对象实例,类似于调用其DEFINITE(元)方法。
为了热身,让我们将:D
应用于我们卑微的Int
示例的右侧
say 42 ~~ Int; # OUTPUT: «True»say Int ~~ Int; # OUTPUT: «False»
请注意,在上面只有42
匹配Int:D
。
返回limit-lines
,我们现在可以修改其签名以尽早捕获错误
sub limit-lines(Str , Int ) ;say limit-lines Str, 3;CATCH ;# 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; # OUTPUT: «False»say Int ~~ Int; # OUTPUT: «True»
现在42
无法匹配Int:U
,而Int
成功匹配。
这里有一个更实际的例子
sub can-turn-into(Str , Any )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 objectsay Int ~~ Any; # OUTPUT: «False»say Int ~~ Any; # OUTPUT: «True»say Int ~~ Any; # OUTPUT: «True»# Checking a subsetof Int where * // 2;say 3 ~~ Even; # OUTPUT: «True»say 3 ~~ Even; # OUTPUT: «False»say Int ~~ Even; # OUTPUT: «True»# Checking an object instancesay 42 ~~ Any; # OUTPUT: «True»say 42 ~~ Any; # OUTPUT: «False»say 42 ~~ Any; # OUTPUT: «True»# Checking a user-supplied class;say Foo ~~ Any; # OUTPUT: «False»say Foo ~~ Any; # OUTPUT: «True»say Foo ~~ Any; # OUTPUT: «True»# Checking an instance of a classmy = Foo.new;say ~~ Any; # OUTPUT: «True»say ~~ Any; # OUTPUT: «False»say ~~ Any; # OUTPUT: «True»
类和对象文档进一步阐述了实例和类型对象的概念,并使用.DEFINITE
方法发现它们。
请记住,所有参数都有值;即使是可选参数也有默认值,这些默认值是显式类型约束的受约束类型的类型对象。如果不存在显式类型约束,则默认值对于方法、子方法和子例程是 Any
类型对象,对于块是 Mu
类型对象。这意味着,如果您使用 :D
类型笑脸,则需要提供默认值或使参数成为必需参数。否则,默认值将是类型对象,这将导致确定性约束失败。
sub divide (Int : = 2, Int :!)divide :1a, :2b; # OUTPUT: «0.5»
当特定参数(位置参数或命名参数)完全没有值时,将启用默认值。
sub f( = 42);f; # OUTPUT: «4242»f Nil; # OUTPUT: «Nilanswer»
$a
的默认值为 42。如果没有值,$a
将被分配在 Signature
中声明的默认值。但是,在第二种情况下,它确实接收了一个值,恰好是 Nil
。将 Nil
分配给任何变量都会将其重置为其默认值,该默认值已通过使用 default 特性声明为 'answer'
。这解释了我们在第二次调用 f
时发生的情况。例程参数和变量以不同的方式处理默认值,这在一定程度上是由每种情况下声明默认值的不同方式(对于参数使用 =
,对于变量使用 default
特性)所阐明的。
注意:在 6.c 语言中,:U
/:D
受约束变量的默认值是具有此类约束的类型对象,该对象不可初始化,因此您不能使用 .=
运算符,例如。
use v6.c;my Int .= 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 .= new: 42; # OUTPUT: «42»
关于术语的结束语:本节讨论了使用类型笑脸 :D
和 :U
来约束参数的确定性。有时,定义性被用作确定性的同义词;这可能会令人困惑,因为这些术语具有细微的差别。
如上所述,确定性关注类型对象和对象实例之间的区别。类型对象始终是不确定的,而对象实例始终是确定的。可以使用 DEFINITE(元)方法验证对象是类型对象/不确定还是对象实例/确定。
确定性应与定义性区分开来,后者关注已定义对象和未定义对象之间的差异。可以使用 defined
方法验证对象是已定义还是未定义,该方法在 Mu
类中实现。默认情况下,类型对象被视为未定义,而对象实例被视为已定义;也就是说:.defined
在类型对象上返回 False
,否则返回 True
。但是,此默认行为可以被子类覆盖。覆盖默认 .defined
行为的子类的示例是 Failure
,因此,即使是实例化的 Failure
也充当未定义值
my = Failure; # Initialize with type objectmy = Failure.new("foo"); # Initialize with object instancesay .DEFINITE; # OUTPUT: «False» : indefinite type objectsay .DEFINITE; # OUTPUT: «True» : definite object instancesay .defined; # OUTPUT: «False» : default responsesay .defined; # OUTPUT: «False» : .defined override
约束 Callable
的签名§
:u
允许空格)
sub apply(:(Int --> Int), Int \n)sub identity(Int \i --> Int)sub double(Int \x --> Int)say apply , 10; # OUTPUT: «10»say apply , 10; # OUTPUT: «20»
带类型的 lambda 也适用于受约束的可调用参数。
say apply -> Int \x --> Int , 3; # OUTPUT: «6»say apply -> Int \x --> Int , 3; # OUTPUT: «27»
请注意,此简写语法仅适用于带有 &
标记的参数。对于其他参数,您需要使用长版本
sub play-with-tens( where .signature ~~ :(Int, Str))sub by-joining-them(Int , Str )play-with-tens ; # OUTPUT: «ten10»play-with-tens -> Int \i, Str \s ; # OUTPUT: «tenten»sub g(Num , Str )# play-with-tens(&g); # Constraint type check failed
约束返回类型§
有多种方法可在 Routine
上约束返回类型。以下所有版本当前均有效,并且将在成功执行例程时强制执行类型检查。
Nil
和 Failure
始终允许作为返回类型,无论任何类型约束如何。这允许 Failure
返回并向下传递调用链。
sub foo(--> Int) ;say foo.raku; # OUTPUT: «Nil»
不支持类型捕获。
返回类型箭头:--
>§
首选在签名中指示返回类型(或常量)的这种形式,因为它可以处理常量值,而其他形式则不能。为了保持一致性,这是本网站上唯一接受的形式。
返回类型箭头必须放在参数列表的末尾,前面可以带或不带 ,
。
sub greeting1(Str --> Str) # Validsub greeting2(Str , --> Str) # Validsub favorite-number1(--> 42) # OUTPUT: 42sub favorite-number2(--> 42) # OUTPUT: 42
如果类型约束是常量表达式,则将其用作例程的返回值。该例程中的任何返回语句都必须没有参数。
sub foo(Str --> 123)my = foo("hello"); # OUTPUT: hellosay ; # OUTPUT: 123
# The code below will not compilesub foo(Str --> 123)my = foo("hello");say ;
returns
§
签名声明后面的关键字 returns
与 -->
具有相同的功能,但需要注意的是,此形式不适用于常量值。你也不能在块中使用它。这就是始终首选尖角箭头形式的原因。
sub greeting(Str ) returns Str # Valid
sub favorite-number returns 42 # This will fail.
of
§
of
只是 returns
关键字的真实名称。
sub foo() of Int ; # Valid
sub foo() of 42 ; # This will fail.
前缀(类似 C)形式§
这类似于对变量施加类型约束,例如 my Type $var = 20;
,除了 $var
是例程的定义。
my Int sub bar ; # Valid
my 42 sub bad-answer ; # This will fail.
强制转换类型§
要接受一种类型但自动将其强制转换为另一种类型,请将接受的类型用作目标类型的参数。如果接受的类型是 Any
,则可以省略它。
sub f(Int(Str) , Str() )f '10', 10;# OUTPUT: «Int Str»sub foo(Date(Str) ) ;foo "2016-12-01";# OUTPUT: «Date2016-12-01»
如果存在,则通过调用具有要强制转换到的类型名称的方法来执行强制转换。在此示例中,我们在 Str
类上调用内置方法 Date
。该方法假定返回正确的类型——当前不会对结果执行其他检查。
强制转换也可以在返回类型上执行
sub square-str (Int --> Str(Int))for 2,4, *² … 256 -># OUTPUT: «2² is 1 figures long# 4² is 2 figures long# 16² is 3 figures long# 256² is 5 figures long»
在此示例中,将返回类型强制转换为 Str
允许我们直接应用字符串方法,例如字符数。
注意:参数上必须有适当的方法,因此在尝试强制转换自定义类型时要小心。
sub bar(Foo(Int) )bar(3);# OUTPUT: «Impossible coercion from 'Int' into 'Foo': no acceptable coercion method found»
Slurpy 参数§
如果一个函数可以接受数量可变的参数,则称其为可变参数,即其元数不是固定的。因此,可选、命名和 slurpy 参数使得使用它们的例程成为可变参数,并由此称为可变参数。这里我们将重点介绍 slurpy 参数,或简单地称为slurpies。
数组或哈希参数可以通过前导单个星号 (*) 或双星号 (**) 或前导加号 (+) 标记为slurpy。slurpy 参数可以绑定到任意数量的参数(零个或更多),并且它将导致与符号兼容的类型。
之所以称为“slurpy”,是因为它们像有人在 slurping 面条一样吸收了函数的任何剩余参数。
my = :(, ); # exactly two arguments, second must be Positionalmy = :(, *); # at least one argument, @b slurps up any beyond thatmy = :(*); # no positional arguments, but any number# of named argumentssub one-arg (@)sub slurpy (*@)one-arg (5, 6, 7); # ok, same as one-arg((5, 6, 7))slurpy (5, 6, 7); # okslurpy 5, 6, 7 ; # ok# one-arg(5, 6, 7) ; # X::TypeCheck::Argument# one-arg 5, 6, 7 ; # X::TypeCheck::Argumentsub named-names (*) ;say named-names :foo(42) :bar<baz>; # OUTPUT: «foo bar»
位置和命名 slurpies 可以组合;命名参数(即,Pair
)收集在指定的哈希中,位置参数收集在数组中
sub combined-slurpy (*, *)# or: sub combined-slurpy (*%h, *@a) { ... }say combined-slurpy(one => 1, two => 2);# OUTPUT: «{array => [], hash => {one => 1, two => 2}}»say combined-slurpy(one => 1, two => 2, 3, 4);# OUTPUT: «{array => [3 4], hash => {one => 1, two => 2}}»say combined-slurpy(one => 1, two => 2, 3, 4, five => 5);# OUTPUT: «{array => [3 4], hash => {five => 5, one => 1, two => 2}}»say combined-slurpy(one => 1, two => 2, 3, 4, five => 5, 6);# OUTPUT: «{array => [3 4 6], hash => {five => 5, one => 1, two => 2}}»
请注意,在 slurpy(或事实上在任何类型的可变参数)参数之后不允许出现位置参数
:(*, );# ===SORRY!=== Error while compiling:# Cannot put required parameter $last after variadic parameters
通常,slurpy 参数将创建一个Array
(或兼容类型),为每个参数创建一个新的Scalar
容器,并将每个参数的值分配给这些Scalar
。如果原始参数也有一个中间Scalar
,则在此过程中将绕过它,并且在被调用的函数中不可用。
Sigiled 参数始终会对收集的参数施加上下文。无 Sigil 参数也可以使用 slurpily,前面加上一个 + 号,以使用它们最初开始的任何初始类型
sub zipi( +zape );say zipi( "Hey "); # OUTPUT: «List => (Hey )»say zipi( 1...* ); # OUTPUT: «Seq => (...)»
当与一些特征和修饰符结合时,slurpy 参数具有特殊行为,如slurpy 数组参数部分中所述。
如果方法没有声明另一个 slurpy 命名参数,则它们会自动获得一个*%_
slurpy 命名参数。
slurpy 数组参数的类型§
slurpy 数组参数有三种变体。
单星号形式展平传递的参数。
双星号形式不展平参数。
加号形式根据单个参数规则展平。
将在接下来的几节中详细描述每个参数。由于每个参数之间的差异有点细微,因此提供了每个参数的示例,以演示每个 slurpy 约定如何与其他约定不同。
展平 slurpy§
使用一个星号声明的 slurpy 参数将通过溶解一层或多层裸Iterable
来展平参数。
my = <a b c>;my := <d e f>;sub a(*) ;a(); # OUTPUT: «["a", "b", "c"]»a(1, , [2, 3]); # OUTPUT: «[1, "d", "e", "f", 2, 3]»a([1, 2]); # OUTPUT: «[1, 2]»a(1, [1, 2], ([3, 4], 5)); # OUTPUT: «[1, 1, 2, 3, 4, 5]»a(( for 1, 2, 3)); # OUTPUT: «[1, 2, 3]»
单个星号 slurpy 展平所有给定的可迭代对象,有效地将使用逗号创建的任何对象提升到顶层。
未展平 slurpy§
使用两个星号声明的 slurpy 参数不会展平列表中的任何Iterable
参数,而是或多或少地保持参数原样
my = <a b c>;my := <d e f>;sub b(**) ;b(); # OUTPUT: «[["a", "b", "c"],]»b(1, , [2, 3]); # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]»b([1, 2]); # OUTPUT: «[[1, 2],]»b(1, [1, 2], ([3, 4], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]»b(( for 1, 2, 3)); # OUTPUT: «[(1, 2, 3),]»
双星号 slurpy 隐藏嵌套的逗号对象,并按原样保留在 slurpy 数组中。
单参数规则 slurpy§
使用加号创建的 slurpy 参数会启用“单参数规则”,该规则会根据上下文决定如何处理 slurpy 参数。基本上,如果只传递了一个参数,并且该参数是 Iterable
,则该参数将用于填充 slurpy 参数数组。在任何其他情况下,+@
的工作方式与 **@
相同。
my = <a b c>;my := <d e f>;sub c(+) ;c(); # OUTPUT: «["a", "b", "c"]»c(1, , [2, 3]); # OUTPUT: «[1, ("d", "e", "f"), [2, 3]]»c([1, 2]); # OUTPUT: «[1, 2]»c(1, [1, 2], ([3, 4], 5)); # OUTPUT: «[1, [1, 2], ([3, 4], 5)]»c(( for 1, 2, 3)); # OUTPUT: «[1, 2, 3]»
有关其他讨论和示例,请参阅 函数的 slurpy 约定。
类型捕获§
类型捕获允许将类型约束的规范推迟到调用函数时。它们允许在签名和函数体中同时引用类型。
sub f(::T , T , ::C)# 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 = f(10, 2, Int.new / Int.new);say s(2); # 10 / 2 * 2 == 10
位置参数与命名参数§
参数可以是位置参数或命名参数。默认情况下,参数是位置参数,但 slurpy 哈希和以冒号 :
开头的参数除外。后者称为 冒号对。检查以下签名及其表示的含义
= :(); # a positional argument= :(:); # a named argument of name 'a'= :(*); # a slurpy positional argument= :(*); # a slurpy named argument
在调用者方面,位置参数按声明参数的顺序传递。
sub pos(, )pos(4, 5); # OUTPUT: «x=4 y=5»
对于命名参数和参数,仅使用名称将参数映射到参数。如果使用粗箭头构造 Pair
,则只有那些具有有效标识符作为键的参数才被识别为命名参数。
sub named(:, :)named( y => 5, x => 4); # OUTPUT: «x=4 y=5»
您可以使用与命名参数同名的变量调用例程;在这种情况下,:
将用于调用,以便将变量的名称理解为参数的键。
sub named-shortcut( : )named-shortcut( shortcut => "to here"); # OUTPUT: «Looks like to here»my = "Þor is mighty";named-shortcut( : ); # OUTPUT: «Looks like Þor is mighty»
命名参数的名称可以与变量名称不同
sub named(:official())named :official;
参数别名§
冒号对 语法可用于为参数提供别名
sub alias-named(:color(:), :type(:class()))alias-named(color => "red", type => "A"); # both names can be usedalias-named(colour => "green", type => "B"); # more than two names are okalias-named(color => "white", class => "C"); # every alias is independent
冒号 :
的存在将决定我们是否创建一个新的命名参数。:$colour
不仅将成为别名变量的名称,还将成为一个新的命名参数(在第二次调用中使用)。但是,$kind
将只是别名变量的名称,不会创建一个新的命名参数。可以在 sub MAIN 中找到更多别名的用法。
具有命名参数的函数可以动态调用,对 Pair
进行解引用,使用 |
将其转换为命名参数。
multi f(:) ;multi f(:) ;for 'named', 'also-named' ->my = :named(1);f |; # OUTPUT: «(:$named)»
同样可以将 Hash
转换为命名参数。
sub f(:) ;my = also-named => 4;f |; # OUTPUT: «(:$also-named)»
当将包含列表的 Hash
滑入命名参数时,可能会出现问题。为避免额外的容器层,请在滑入之前强制转换为 Map
。
;my = <x y z> Z=> (5, 20, [1,2]);say C.new(|.Map);# OUTPUT: «C.new(x => 5, y => 20, z => [1, 2])»
您可以为命名参数创建任意多个别名
sub alias-named(:color(:),:variety(:style(:sort(:type(:class())))))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 is raw, :timestamped($)!)sub foo is timestampedfoo;say +&foo.?timestamps; # OUTPUT: «1»
可选参数和必需参数§
默认情况下,位置参数是必需的,可以通过默认值或尾随问号使其变为可选的
= :(Str ); # required parameter= :( = 10); # optional parameter, default value 10= :(Int ?); # optional parameter, default is the Int type object
默认情况下,命名参数是可选的,可以通过尾随感叹号使其变为强制性的
= :(:); # optional parameter= :(: = False); # optional parameter, defaults to False= :(:!); # mandatory 'name' named parameter
默认值可以依赖于先前的参数,并且(至少在概念上)为每个调用重新计算
= :(, = / 100);= :(: = ['.', '..']); # 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 ( = PositionalAt)a; # OUTPUT: «True»a [1, 2, 3]; # OUTPUT: «False»
带有某些标量参数的示例
my constant AnyAt = Any.new;sub b (=AnyAt, :=AnyAt)b 1; # OUTPUT: «FalseTrue»b 1, :2y; # OUTPUT: «FalseFalse»
如果您的参数已输入,则 类型笑脸 可与 multi
一起使用,如下所示
multi c (Int )multi c (Int )multi c (Int ?)c; #Omittedc (Int); #Undefinedc 42; #Defined
这些示例使用诸如 PositionalAt
之类的名称来反映 .WHICH
测试返回类型为 ObjAt
的对象,您可以自由地创建自己的名称。
动态变量§
动态变量 允许在签名中使用,尽管它们不提供特殊行为,因为参数绑定无论如何都会连接两个范围。
解构参数§
非标量参数可以后跟或替换括号中的子签名,这将解构给定的参数。列表的解构只是它的元素
sub first( (, *))
或
sub first([, *@])
而散列的解构是它的对
sub all-dimensions(% (:length(:), :width(:), :depth(:)))
尖角循环还可以解构散列,允许赋值给变量
my = (:40life, :41universe, :42everything);for -> (:, :)# OUTPUT: «universe → 41life → 40everything → 42»
通常,对象根据其属性进行解构。一个常见的惯用语是在 for 循环中解包 Pair
的键和值
for <Peter Paul Merry>.pairs -> (:key(), :value())
但是,将对象解包为其属性只是默认行为。要使对象以不同的方式进行解构,请更改其 Capture
方法。
子签名§
要匹配复合参数,请使用括号中参数名称后面的子签名。
sub foo(|c(Int, Str));foo(42, "answer");# OUTPUT: «called with \(42, "answer")»
长名称§
要排除某些参数在多重分派中被考虑,请用双分号将它们分开。
multi f(Int , Str ;; :) ;f(10, 'answer');# OUTPUT: «10, answer, Any»
捕获参数§
用竖线 |
给参数加前缀,会使该参数成为一个 Capture
,用尽所有剩余的位置和命名参数。
这通常用于 proto
定义(如 proto foo (|) {*}
),表示例程的 multi
定义 可以具有任何 类型约束。请参阅 proto 以获取示例。
如果绑定到变量,可以使用滑移运算符 |
将参数作为一个整体转发。
sub a(Int , Str )sub b(|c)b(42, "answer");# OUTPUT: «CaptureInt Str»
参数特征和修饰符§
默认情况下,参数绑定到其参数并标记为只读。可以使用参数上的特征来更改它。
is copy
特征导致复制参数,并允许在例程内对其进行修改
sub count-up( is copy)
is rw
特征代表 is read-write,使参数绑定到变量(或其他可写容器)。对参数赋值会更改调用方侧变量的值。
sub swap( is rw, is rw)
对于 slurpy 参数,is rw
被保留供语言设计者将来使用。
is raw
特征 会自动应用于使用 反斜杠 或 加号 作为“sigil”声明的参数,也可以用来使通常使用 sigil 的参数表现得像这些参数一样。在 slurpy 的特殊情况下,它通常会生成一个由 Scalar
填充的 Array
,如上所述,is raw
反而会使参数生成一个 List
。该列表的每个元素都将直接绑定为原始参数。
要明确要求只读参数,请使用 is readonly
特征。请注意,这仅适用于容器。内部对象很可能具有变异器方法,并且 Raku 不会对对象的属性强制实施不可变性。
特征后面可以跟 where 子句
sub ip-expand-ipv6( is copy where m:i/^**3..39$/)