Perl6:函数/子例程的可变数量的参数 [英] Perl6: variable number of arguments to function/subroutine
问题描述
我希望能够在 Perl6 中运行具有可变数量参数的函数,但是在阅读了 https://docs.perl6.org/language/functions#Arguments 我不知道怎么做.我看到了许多其他语言的链接,并警告说具有类似标题的问题经常被否决",但我在文档或 StackOverflow 的任何地方都没有看到这一点.
I want to be able to run a function with a variable number of parameters in Perl6, but after reading through https://docs.perl6.org/language/functions#Arguments I don't see how it can be done. I see numerous links for other languages, and a warning that "Questions with similar titles have frequently been downvoted" but I don't see this anywhere in the documentation or StackOverflow.
我想做这样的事情:
some-test($v1, $v2)
或
some-test($v3)
但没有单独的函数使用 multi
为每个
but not have a separate function using multi
for each
如何构造一个 Perl6 子例程来接受可变数量的字符串?
How can I construct a Perl6 subroutine which will accept a variable number of strings?
推荐答案
TL;DR 你问的是可变参数函数.1简单的使用就是简单的.一些 P6 特性,最显着的是位置和命名参数,以及可变参数解构,增加了一些皱纹.另外,请参阅其他也非常有帮助的答案.
TL;DR You're asking about variadic functions.1 Simple use is simple. Some P6 features, most notably positional and named arguments, and variadic destructuring, add some wrinkles. Also, see the other answers which are very helpful too.
可变数量的参数
简单的可变参数函数的简单使用:
Simple use of a simple variadic function:
sub variadic (|args) { say args .elems }
variadic(); # 0
variadic('1'); # 1
variadic('1', '2'); # 2
一个 |foo
参数将所有剩余的参数吸进一个 Capture
绑定到无符号标识符 foo
:
A |foo
parameter slurps up all remaining arguments into a Capture
bound to sigilless identifier foo
:
sub variadic ($a, @b, %c, |others) { say others[0] }
variadic 1, [2,3], (:foo), 4, [5,6], (:bar); # 4
在上面的示例中,others
参数slurps"1 从 4
开始列出最后三个参数.
In the above example the others
parameter "slurps"1 up the last three listed arguments starting with the 4
.
事情并不总是那么简单:
Things aren't always simple:
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)
在这个答案的其余部分我解释:
In the rest of this answer I explain:
约束参数,例如确保它们都是字符串.
Constraining arguments, eg ensuring they're all strings.
位置 vs 命名参数;**@foo
和 *%foo
可变位置解构;+@foo
和 *@foo
可变数量的字符串
您可以使用 对 slurped 参数施加任何您想要的约束where
子句.(你可以反过来打包成一个 subset
如果你愿意.)
You can impose any constraints you want on slurped arguments by using a where
clause. (Which you can in turn package into a subset
if you want.)
例如,将所有参数限制为 Str
类型:
For example, to constrain all the arguments to be of type 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
位置 vs 命名参数;**@foo
和 *%foo
P6 支持 positional 和 named 参数.
Positional vs named arguments; **@foo
and *%foo
P6 supports both positional and named arguments.
在签名中使用 |foo
总是会捕获所有剩余的参数,包括位置参数和命名参数:
Using |foo
in a signature always captures all remaining arguments, both positional and named:
sub slurps-into-WHAT (|args) { say WHAT args }
slurps-into-WHAT(); # (Capture)
A Capture
将位置参数存储在可通过以下方式访问的内部列表中位置下标(即 args[...]
)和可通过关联下标访问的哈希中的命名参数(即 args<...>
或 args{...}
):
A Capture
stores positional arguments in an internal list accessible via positional subscripting (i.e. args[...]
) and named arguments in a hash accessible via associative subscripting (i.e. args<...>
or args{...}
):
sub variadic (|args) { say " {args[1]} {args<named2>}" }
variadic('pos0', 'pos1', named1 => 42, named2 => 99); # pos1 99
有时最好只收集命名参数,或只收集位置参数,或者收集两者但在单独的参数中.
Sometimes it's preferable to collect just named args, or just positional ones, or to collect both but in separate parameters.
要收集命名参数,请使用 *%foo
形式的参数(一个星号前缀和一个哈希参数):
To collect named args, use a parameter of the form *%foo
(one asterisk prefix and a hash arg):
sub variadic-for-nameds (*%nameds) { say %nameds }
variadic-for-nameds(:foo, :bar); # {bar => True, foo => True}
(注意,所有方法都以这种方式收集命名参数,即使它们的签名没有明确说明.2)
(Note that all methods collect named args in this way even if their signature doesn't explicitly say so.2)
要收集位置参数,请使用 **@foo
形式的参数(两个星号前缀紧跟一个数组参数):
To collect positional args, use a parameter of the form **@foo
(two asterisk prefix immediately followed by an array arg):
sub variadic-for-positionals (**@positionals) { say @positionals }
variadic-for-positionals(1, 2, 3); # [1 2 3]
可变位置解构;+@foo
和 *@foo
P6 提供了一系列非可变参数参数解构功能.
第一个可变位置解构参数形式是+@foo
.除了一种情况外,这与 **@foo
的效果完全相同;如果可变参数只有一个参数,并且该参数是一个列表或数组,则该参数绑定到该列表或数组的内容,剥离列表/数组容器:
The first variadic positionals destructuring parameter form is +@foo
. This has exactly the same effect as **@foo
except in one case; if the variadic parameter gets just a single argument, and that argument is a list or array, then the parameter is bound to the content of that list or array, stripping away the list/array container:
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
表单是为了支持 "单一参数规则.它由编写内置插件的核心开发人员使用.用户可能希望在他们想要相同的行为时使用它.
The +@foo
form was introduced to support the "single arg rule". It's used by core devs writing built ins. Users may wish to use it when they want the same behavior.
另一种可变参数位置解构形式是*@foo
.它与 +@foo
做同样的事情,因为它从列表或数组容器 args 中提取内容并将容器扔掉.但它更具侵略性:
The other variadic positionals destructuring form is *@foo
. It does the same thing as +@foo
in that it extracts the content from list or array container args and throws the container away. But it's much more aggressive:
它对所有参数都这样做.
It does this for all arguments.
如果一个参数是一个列表而不是一个数组((...)
而不是 [...]
),那么它会下降到那个列表中如果列表的元素本身就是另一个内部列表或数组,则递归地重复该练习.
If an argument is a list rather than an array ((...)
rather than [...]
) then it descends into that list and recursively repeats the exercise if an element of the list is itself another inner list or array.
因此:
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)]
数组中剥离容器,但没有下降到其中.)
(Note how it stripped the container from the [5,6,(7,8)]
array but did not descend into it.)
最后一件事;是否有可变参数命名的解构参数?你告诉我.3
One final thing; are there variadic named destructuring parameters? You tell me.3
(我加入了这个奖励部分,希望它能避免混淆.如果这个部分本身令人困惑,请忽略它.)
(I included this bonus section in the hope it heads off confusion. If this section is itself confusing, just ignore it.)
例程调用可以在其参数列表周围使用或不使用括号编写,它们的含义相同.左括号必须紧跟在例程名称之后,中间不能有空格:
Routine calls can be written with or without parentheses around their list of arguments and they mean the same thing. The opening parenthesis must follow the routine name immediately, without intervening whitespace:
sub foo (|args) { say args[0] }
foo 'a', 'b'; # a
foo('a', 'b'); # a
(此规则仅适用于例程调用,在例程名称与其参数之间.不适用于例程声明, 在例程名称和它的参数之间.对于后者,声明,你可以不留空格或使用空格,就像我上面的 sub foo (|args)
和没什么区别.)
(This rule only applies to the routine call, between the routine name and its arguments. It does not apply to the routine declaration, between the routine name and its parameters. For the latter, the declaration, you can leave no space or use space as I have above with sub foo (|args)
and it makes no difference.)
如果您在调用中的 foo
和左括号之间插入空格,您将编写不同的内容:
If you insert whitespace between the foo
and the opening parenthesis in a call you're writing something different:
foo ('a', 'b'); # (a b)
使用 one 参数调用 foo
,一个列表 ('a', 'b')
与 foo 形成对比('a', 'b')
使用 两个 参数调用 foo
,括号内的两个值.
That calls foo
with one argument, the one list ('a', 'b')
in contrast to foo('a', 'b')
which calls foo
with two arguments, the two values inside the parentheses.
以下使用两个参数调用foo
,这两个参数都是列表:
The following calls foo
with two arguments, both of which are lists:
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') )
(恰好包含两个内部列表).
The latter two foo
calls get one argument, the one list ( ('a', 'b', 'c'), ('d', 'e', 'f') )
(that happens to contain two inner lists).
1 对此的标准行业术语是 可变参数函数.在撰写此答案时,Rakudo P6 编译器在错误消息中使用行业标准术语(variadic"),但官方 P6 文档倾向于使用slurpy"这个词.而不是可变参数"和谈论大吵大闹".
1 The standard industry term for this is a variadic function. At the time of writing this answer, the Rakudo P6 compiler uses the industry standard term ("variadic") in error messages but the official P6 doc tends to use the word "slurpy" instead of "variadic" and talks of "slurping up arguments".
2 如果没有明确指定,方法总是有一个名为 %_
的隐式可变参数命名参数:
2 Methods always have an implicit variadic named parameter called %_
if one is not explicitly specified:
say .signature given my method foo {} # (Mu: *%_)
3 P6 语言和/或 Rakudo P6 编译器当前允许以 +%foo
和 **%foo
的形式编写参数代码>.让可变参数命名解构真的没有意义.也许这就解释了为什么这两种形式都会做疯狂的事情:
3 The P6 language and/or the Rakudo P6 compiler currently allows parameters to be written in the form +%foo
and **%foo
. It doesn't really make sense to have variadic nameds destructuring. Perhaps that explains why both these forms do insane things:
**%foo
似乎与%foo
没有区别.
**%foo
appears to be indistinguishable from%foo
.
+%foo
似乎与 **@foo
没有区别,除了使用标识符 %foo
而不是 >@foo
.绑定到 %foo
的对象是一个 Array
!
+%foo
appears to be indistinguishable from **@foo
except for using the identifier %foo
instead of @foo
. The object bound to %foo
is an Array
!
这篇关于Perl6:函数/子例程的可变数量的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!