我希望能够在Perl6中运行具有可变数量参数的函数,但在通过https://docs.perl6.org/language/functions#Arguments读取之后,我看不出它是如何完成的。我看到其他语言的链接很多,并且警告“类似标题的问题经常被低估”但我在文档或StackOverflow中没有看到这一点。
我想做这样的事情:
some-test($v1, $v2)
要么
some-test($v3)
但没有为每个使用multi
的单独功能
如何构造一个接受可变数量字符串的Perl6子程序?
TL; DR你问的是可变函数.1简单的使用很简单。一些P6特性,最值得注意的是位置和命名参数,以及可变参数解构,增加了一些皱纹。另外,请参阅其他非常有用的答案。
可变数量的参数
简单使用简单的可变函数:
sub variadic (|args) { say args .elems }
variadic(); # 0
variadic('1'); # 1
variadic('1', '2'); # 2
一个|foo
参数将所有剩余的参数放入一个Capture
绑定到无标识符foo
:
sub variadic ($a, @b, %c, |others) { say others[0] }
variadic 1, [2,3], (:foo), 4, [5,6], (:bar); # 4
在上面的例子中,others
参数“slurps”1以4
开头的最后三个列出的参数。
事情并不总是那么简单:
variadic(1, 2); # 2 -- works but args are Ints
variadic(:foo,:bar); # 0 -- where did :foo, :bar go?
variadic((:foo)); # 1 -- works; arg is Pair (:foo)
variadic((1, 2)); # 1 -- works; arg is List (1,2)
在这个答案的其余部分,我解释说:
**@foo
和*%foo
+@foo
和*@foo
可变数量的字符串
您可以使用where
子句对slurped参数强加任何约束。 (如果你愿意,你可以将其打包成subset
。)
例如,要将所有参数约束为Str
类型:
sub variadic (|args where .all ~~ Str) { say args .elems }
variadic(); # 0
variadic('1'); # 1
variadic('1', '2'); # 2
variadic(1); # Constraint type check failed
**@foo
and *%foo
P6同时支持positional and named arguments。
在签名中使用|foo
始终捕获所有剩余的参数,包括位置和命名:
sub slurps-into-WHAT (|args) { say WHAT args }
slurps-into-WHAT(); # (Capture)
Capture
将位置参数存储在可通过位置下标(即args[...]
)访问的内部列表中,并通过关联下标(即args<...>
或args{...}
)访问哈希中的命名参数:
sub variadic (|args) { say " {args[1]} {args<named2>}" }
variadic('pos0', 'pos1', named1 => 42, named2 => 99); # pos1 99
有时最好只收集命名的args,或者只收集位置的args,或者收集两者但是在单独的参数中。
要收集命名args,请使用*%foo
形式的参数(一个星号前缀和一个哈希arg):
sub variadic-for-nameds (*%nameds) { say %nameds }
variadic-for-nameds(:foo, :bar); # {bar => True, foo => True}
(请注意,所有方法都以这种方式收集命名args,即使它们的签名没有明确说明。)
要收集位置参数,请使用**@foo
形式的参数(两个星号前缀后面紧跟一个数组arg):
sub variadic-for-positionals (**@positionals) { say @positionals }
variadic-for-positionals(1, 2, 3); # [1 2 3]
+@foo
and *@foo
P6提供了一系列非可变的argument destructuring features。
第一个可变位置解构参数形式是+@foo
。除了一个案例外,这与**@foo
具有完全相同的效果;如果variadic参数只获得一个参数,并且该参数是一个列表或数组,则该参数绑定到该列表或数组的内容,剥离列表/数组容器:
sub variadic-plus (+@positionals) { say @positionals }
variadic-plus(1,2,3); # says same as for **@positionals
variadic-plus(1); # says same as for **@positionals
variadic-plus([1]); # [1] -- instead of [[1]]
variadic-plus((1,2,3)); # [1 2 3] -- instead of [(1 2 3)]
引入+@foo
形式以支持"single arg rule"。核心开发人员使用它编写内置的ins。用户可能希望在他们想要相同的行为时使用它。
其他可变位置解构形式是*@foo
。它与+@foo
的作用相同,它从列表或数组容器args中提取内容并将容器抛弃。但它更具侵略性:
(...)
而不是[...]
),那么它会下降到该列表中,并且如果列表的元素本身是另一个内部列表或数组,则递归地重复该练习。从而:
sub variadic-star (*@positionals) { say @positionals }
variadic-star((1,2),[3,4]); # [1 2 3 4]
variadic-star((1,2),(3,4,(5,6,(7,8)))); # [1 2 3 4 5 6 7 8]
variadic-star((1,2),(3,4,[5,6,(7,8)])); # [1 2 3 4 5 6 (7 8)]
(注意它是如何从[5,6,(7,8)]
数组中剥离容器但是没有下降到它中。)
最后一件事;有可变的命名解构参数吗?你告诉我
foo(...)
vs foo (...)
(我包括了这个奖金部分,希望它能避免混淆。如果这部分本身令人困惑,那就忽略它。)
常规调用可以在其参数列表周围写入或不加括号,它们的含义相同。左括号必须立即遵循例程名称,而不插入空格:
sub foo (|args) { say args[0] }
foo 'a', 'b'; # a
foo('a', 'b'); # a
(此规则仅适用于例程名称及其参数之间的例程调用。它不适用于例程名称及其参数之间的例程声明。对于后者,声明,您可以不留空间或使用空间正如我在上面与sub foo (|args)
,它没有任何区别。)
如果你在调用中的foo
和左括号之间插入空格,那么你会写一些不同的东西:
foo ('a', 'b'); # (a b)
这称为foo
有一个参数,一个列表('a', 'b')
与foo('a', 'b')
相反,foo
用两个参数调用foo
,括号内的两个值。
以下调用带有两个参数的foo ('a', 'b', 'c'), ('d', 'e', 'f'); # (a b c)
,这两个参数都是列表:
foo( ('a', 'b', 'c'), ('d', 'e', 'f') ) ; # (a b c)
foo (('a', 'b', 'c'), ('d', 'e', 'f')) ; # ((a b c) (d e f))
foo( (('a', 'b', 'c'), ('d', 'e', 'f')) ) ; # ((a b c) (d e f))
您可以嵌套括号:
foo
后两个( ('a', 'b', 'c'), ('d', 'e', 'f') )
调用得到一个参数,一个列表variadic function(恰好包含两个内部列表)。
1标准的行业术语是%_
。在撰写本答案时,Rakudo P6编译器在错误消息中使用了行业标准术语(“可变参数”),但官方P6文档倾向于使用“slurpy”而不是“variadic”这个词,并谈论“争论” ”。
2如果未明确指定方法,则方法始终具有名为say .signature given my method foo {} # (Mu: *%_)
的隐式可变参数命名参数:
+%foo
3 P6语言和/或Rakudo P6编译器目前允许以**%foo
和**%foo
形式编写参数。拥有可变参数名称解构并没有多大意义。也许这就解释了为什么这两种形式都做了疯狂的事情:
%foo
似乎与+%foo
无法区分。**@foo
而不是%foo
之外,@foo
似乎与%foo
无法区分。绑定到Array
的对象是sub some-test( $v1, $v2? ) { ... }
!如果您只想获取1或2个值,那么最简单的方法是将第二个值标记为可选:
sub some-test( $v1, $v2="default" ) { ... }
或者您可以定义默认值:
sub some-test( *@v where *.elems > 0 ) { ... }
或者,如果您想要任意数量的值(1或更多),您可以使用带有where子句的slurpy:
destructuring
您可以使用签名sub some-test(*@all [$first, *@rest]) { ... } # must have 1 or more parameters
qazxswpoi