Perl6:函数/子例程的可变数量的参数 [英] Perl6: variable number of arguments to function/subroutine

查看:43
本文介绍了Perl6:函数/子例程的可变数量的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在 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"14 开始列出最后三个参数.

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 支持 positionalnamed 参数.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆