Perl6:函数/子例程

时间:2019-02-15 15:30:41

标签: perl6

我希望能够在Perl6中运行带有可变数量参数的函数,但是在通读https://docs.perl6.org/language/functions#Arguments之后,我看不到如何实现。我看到许多其他语言的链接,并警告说“标题相似的问题经常被低估”,但是我在文档或StackOverflow的任何地方都看不到这一点。

我想做这样的事情:

some-test($v1, $v2)

some-test($v3)

但没有单独的功能,每个功能都使用multi

如何构造一个 Perl6 子例程,该例程将接受可变数量的字符串?

3 个答案:

答案 0 :(得分:9)

TL; DR 您正在询问可变参数函数。 1 简单使用很简单。 P6的某些功能(最引人注目的是位置和命名参数)以及可变参数解构会增加一些皱纹。另外,请参阅其他答案,它们也非常有用。

  

可变数量的参数

简单使用简单的可变参数函数:

sub variadic (|args) { say args .elems }
variadic();           # 0
variadic('1');        # 1
variadic('1', '2');   # 2

一个|foo参数将所有剩余参数混入绑定到无符号标识符foo的{​​{3}}中:

sub variadic ($a, @b, %c, |others) { say others[0] }
variadic 1, [2,3], (:foo), 4, [5,6], (:bar); # 4

在上面的示例中,others参数从前面的4开始,在最后三个列出的参数中“吸食” 1

可变参数

事情并不总是那么简单:

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

约束参数

  

可变数量的字符串

您可以通过使用Capture子句将任何想要的约束强加给含糊的参数。 (如果需要,您可以依次将其打包为where。)

例如,要将所有参数约束为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

Postional named 自变量; **@foo*%foo

P6同时支持subset

在签名中使用|foo总是会捕获所有剩余的参数,包括位置参数和命名参数:

sub slurps-into-WHAT (|args) { say WHAT args }
slurps-into-WHAT(); # (Capture)

positional and named arguments将位置参数存储在可通过位置下标访问的内部列表中(即args[...]),将命名参数存储在可通过关联下标访问的哈希中(即args<...>或{{1} }):

args{...}

有时候,最好只收集命名的args,或者只收集位置的args,或者收集两个args,但是要使用单独的参数。

要收集命名参数,请使用sub variadic (|args) { say " {args[1]} {args<named2>}" } variadic('pos0', 'pos1', named1 => 42, named2 => 99); # pos1 99 形式的参数(一个星号前缀和一个哈希arg):

*%foo

(请注意,即使 all 方法的签名没有明确说明,它们也会以这种方式收集命名的args。 2

要收集位置参数,请使用sub variadic-for-nameds (*%nameds) { say %nameds } variadic-for-nameds(:foo, :bar); # {bar => True, foo => True} 形式的参数(两个星号前缀,后跟一个数组arg):

**@foo

可变位置分解; sub variadic-for-positionals (**@positionals) { say @positionals } variadic-for-positionals(1, 2, 3); # [1 2 3] +@foo

P6提供了一系列非变量Capture

第一个可变参数位置分解参数形式为*@foo。除了一种情况外,这与+@foo的作用完全相同;如果可变参数仅获得一个参数,并且该参数是列表或数组,则该参数将绑定到该列表或数组的内容,从而剥离列表/数组容器:

**@foo

引入了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)] 表单来支持argument destructuring features。由核心开发人员编写内置插件使用。用户可能希望在想要相同的行为时使用它。

其他可变参数位置分解形式为+@foo。它与*@foo的作用相同,它从列表或数组容器args中提取内容并将其丢弃。但这要更具侵略性:

  • 它对所有参数都执行此操作。

  • 如果参数是列表而不是数组(+@foo而不是(...)),则它会下降到该列表中,如果列表中的元素本身是另一个元素,则递归重复该练习内部列表或数组。

因此:

[...]

(请注意,它是如何从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)] 数组中删除容器的,但没有下降到该数组中。)

最后一件事情;是否有可变的命名解构参数?你告诉我。 3

奖励部分:[5,6,(7,8)]foo(...)

(我将这个奖金部分包括进来,希望它能避免造成混乱。如果此部分本身令人困惑,请忽略它。)

例程调用的参数列表可以带或不带括号,它们的含义相同。开头的括号必须立即遵循例程名称,且不得插入空格:

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')调用一个带有两个< / strong>参数,括号内的两个值。

以下调用带有两个参数的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)) (恰好包含两个内部列表)。

脚语

1 的标准行业术语是"single arg rule"。在撰写此答案时,Rakudo P6编译器在错误消息中使用了行业标准术语(“可变”),但是官方的P6文档倾向于使用单词“ slurpy”而不是“ variadic”,并谈论“混淆参数” “。

2 如果未明确指定,则方法始终具有一个名为foo的隐式可变参数命名参数:

( ('a', 'b', 'c'),  ('d', 'e', 'f') )

3 当前,P6语言和/或Rakudo P6编译器允许以%_say .signature given my method foo {} # (Mu: *%_) 的形式编写参数。杂乱无章的名字进行销毁确实没有任何意义。也许这解释了为什么这两种形式都做疯狂的事情:

  • +%foo**%foo似乎无法区分。

  • **%foo%foo似乎没有区别,除了使用标识符+%foo而非**@foo。绑定到%foo的对象是@foo

答案 1 :(得分:7)

如果只想采用1或2个值,那么最简单的方法是将第二个值标记为可选:

sudo apt-get install r-cran-car

或者您可以定义默认值:

sub some-test( $v1, $v2? ) { ... }

或者如果您想要任意数量的值(1个或多个),则可以在带有where子句的情况下使用slurpy:

sub some-test( $v1, $v2="default" ) { ... }

答案 2 :(得分:7)

您可以使用签名destructuring

sub some-test(*@all [$first, *@rest]) { ... } # must have 1 or more parameters