这两个函数调用约定有什么区别?

What's the difference these two function calling conventions?

可以通过几种方式调用函数:

say(1, 2, 3) # 123
say: 1, 2, 3 # (1, 2, 3)

后者似乎通过了Positional,但除此之外我不知道它们还有什么不同。有什么重要的区别需要知道吗?您会在哪些情况下使用其中一种?

正如 Raiph 在上面告诉您的那样,say: 是一个标签。所以你没有 say 任何东西(即使你认为你做了)并且 - 在 REPL 的使用之外 - 编译器会抱怨你对 <a b c> 的使用是无用的:

say: <a b c>; # OUTPUT: «WARNINGS for <tmp>:␤Useless use of constant value a b c in sink context (lines 1, 1, 1, 1, 1, 1)␤»

但是,您通常 可以 在方法调用中使用 : 表示法而不是括号。考虑下面的四个例程调用(两个子例程调用然后两个方法调用):

my @numbers = (33, 77, 49, 11, 34);
say map  *.is-prime, @numbers  ;  # simplest subroutine call syntax
say map( *.is-prime, @numbers );  # same meaning, but delimiting args
say @numbers.map( *.is-prime ) ;  # similar, but using .map *method*
say @numbers.map: *.is-prime   ;  # same, but using : instead of parens

这些句子都会return相同(False False False True False)

一般来说,正如您在上面看到的 map,您可以在方法调用中使用 () 来使用 :,但反之则不然; : 只能在方法调用中使用。

如果参数需要精确定界,请使用 (),如下 Raiph 评论。

这个答案侧重于基础知识。有关例程调用语法的精确细节的更详尽的介绍,请参阅 Raiph 的回答。 (作为一个重要示例,如果例程名称和冒号 (:) 或左括号 (() 之间有任何空格,这些调用的含义通常会发生变化)。

@jjmerelo 的回答涵盖了基础知识。这个补充性答案旨在提供一些详尽的信息,但希望不会详尽,涵盖陷阱、罕见情况和建议。

foo: valuea, valueb, ...

也许令人惊讶的是,这 不是 对名为 foo.

的子方法或方法的调用

而是一个以 labelfoo:.

开头的语句

您问题中的 say: 行在普通程序中不起作用:

say: <a b c>; # Useless use of constant value a b c ...

“无用”警告意味着 <a b c> 没有以有用的方式使用。 say: 没有对值列表做任何事情。它只是一个标签,什么都不做。

推测您正在使用类似 Perl 6 REPL 的东西。 REPL 会自动 say 设置行中的最后一个值(如果未以其他方式使用),从而使该行看起来可以在没有警告的情况下工作。

.a-method:

如果 postfix method call using the form .a-method has no arguments other than the invocant (the argument to the left of the ., or the current topic 如果没有明确的调用者)那么你可以将其写成以下形式:

42.say ;

您可以选择附加一个冒号:

42.say: ;

没有充分的理由,但符合:

.a-method: arg2, arg3, ...

如果你想为后缀 .a-method 调用提供一个或多个参数(调用者除外),那么你必须选择两种引入它们的方法之一。

一种方法是在方法名之后、参数之前立即写一个冒号。方法名和冒号之间不能有space,方法参数前的冒号后必须有space1

例如,以下方法调用中的 Numeric 参数前使用冒号:

say <abc 2 def ghi> .first: Numeric ; # 2

在上面的行中,方法调用表达式 (.first: Numeric) 在语句终止符 (;) 处结束。如果有一个封闭的子表达式,例如数组下标,则方法调用表达式在该子表达式的末尾结束:

say .[1 + .first: Numeric] given <abc 2 def ghi> ; # ghi

冒号形式方法调用的参数列表也由有效的 statement modifier 结束,例如 given:

say .first: Numeric given <abc 2 def ghi> ; # 2

a-sub arg1, arg2, ...

这是子程序调用的对应形式。唯一的格式差异是子名称前没有调用者或 . 并且您 必须省略 子名称后的冒号。

.a-method( arg2, arg3, ... )

a-sub( arg1, arg2, ... )

用于方法和子调用的另一种常见形式是紧跟在方法或子名称之后,并用括号分隔参数。左括号 必须紧跟在 之后,例程名称和 (.

之间没有任何 space

这里是 .first 方法使用的括号:

say 1 + .first(Numeric) given <abc 2 def ghi> ; # 3

这样做的好处是可以说它比使用外括号的替代方法更漂亮:

say 1 + (.first: Numeric) given <abc 2 def ghi> ; # 3

如果要将子调用直接放在双引号字符串中,需要在子名称前加上 & 标记并使用后缀括号形式:

my @array = <abc 2 def ghi> ;
say "first number is &first(Numeric,@array)" ; # first number is 2

要进行方法调用,您必须再次使用后缀括号形式,并且还必须提供显式调用者(不能只写 "Some text .a-method()"):

my @array = <abc 2 def ghi> ;
say "first number is @array.first(Numeric)" ; # first number is 2

如果没有参数(方法调用的调用者除外),如果您想在字符串中插入子函数或方法调用,您仍然需要使用带有空括号的这种形式:

my @array = <abc 2 def ghi> ;
say "no method call @array[3].uc" ;     # no method call ghi.uc
say "with method call @array[3].uc()" ; # with method call GHI
say "&rand";                            # &rand
say "&rand()";                          # 0.929123203371282

.a-method ( arrgh, arrgh, ... ) ;

这行不通。

因为 .a-method 后面没有冒号,所以方法调用被认为是完整的。

这意味着接下来的事情必须是 expression/statement 像 ; 这样的词尾运算符,或者是将对方法调用结果进行操作的后缀运算符,或者是将对方法调用结果进行运算的中缀运算符结果和一些后续参数。

但是 ( arrgh, arrgh, ... ) 是其中的 none。所以你会得到一个“连续两个术语”的编译错误。

.a-method:( arrgh, arrgh, ... ) ;

.a-method: ( arrgh, arrgh, ... ) ;

一般来说,在方法调用中,不要混合使用 : 和参数周围的括号。没有充分的理由这样做,因为它要么不起作用,要么只是偶然起作用,或者起作用但很可能会使读者感到困惑。

在冒号和左括号之间没有 space 的情况下这样做会产生一个神秘的编译错误:

This type (QAST::WVal) does not support positional operations

离开 space 似乎可行——但通常只能靠运气:

say .first: (Numeric) given <abc 2 def ghi> ; # 2

(Numeric) 是括号中的单个值,它产生 Numeric 所以这一行与:

相同
say .first: Numeric given <abc 2 def ghi> ; # 2

但是如果括号中有两个或更多参数,事情就会出错。使用以下形式之一:

say .first: Numeric, :k given <abc 2 def ghi> ; # 1
say .first(Numeric, :k) given <abc 2 def ghi> ; # 1

正确生成 2 元素的数组索引(“键”)而不是:

say .first: (Numeric, :k) given <abc 2 def ghi> ; # Nil

产生 Nil 因为 .first 方法对 单个 参数没有任何用处,该参数是 [=69= 形式的列表].

当然,您有时可能想要传递一个参数,即括号中的值列表。但是您可以在不使用冒号的情况下这样做。为了清楚起见,我建议您将其写成:

invocant.a-method(( valuea, valueb, ... ));

a-sub ( arrgh1, arrgh2, ... ) ;

正如刚才对方法调用的解释,这将一个参数传递给 a-sub,即单个列表 ( arrgh1, arrgh2, ... ),这很少是作者的意思。

同样,我的建议是将其写成:

`a-sub( valuea, valueb, ... ) ;`

或:

`a-sub  valuea, valueb, ...   ;`

如果您要传递多个参数,或者如果您希望将列表作为单个参数传递,那么:

`a-sub(( valuea, valueb, ... )) ;`

.a-method : arrgha, arrghb, ...

a-sub : arrgha, arrghb, ...

对于方法形式,这会给您带来“混淆”编译错误。

如果 a-sub 不带参数,子表单也是如此。如果 a-sub 接受参数,你会得到一个“前面的上下文需要一个术语,但发现了中缀:而不是”编译错误。

.&a-sub

有一种调用形式可以让您调用声明为子程序的例程——但使用 .method 调用语法。以下将点左侧的“invocant”qux 作为第一个参数提供给名为 a-sub 的子程序:

qux.&a-sub

像往常一样使用 : 或括号将 附加 参数传递给 a-sub:

sub a-sub ($a, $b) { $a == $b }
say 42.&a-sub(42), 42.&a-sub(43); # TrueFalse
say 42.&a-sub: 42;                # True

(在我本节的原始版本中,我写道不能传递额外的参数。我已经测试过这个并且认为不能。但我一定是被什么东西弄糊涂了。@Enheh 的评论让我重新测试并发现 可以 传递额外的参数,就像普通方法调用一样。谢谢 @Enheh。:))

a-method( invocant: arg2, arg3, ... )

a-method invocant: arg2, arg3, ...

在设计文档中称为“间接对象表示法”,这些格式是一种未记录且很少见的方法调用形式,其中调用模仿方法声明——首先是方法名称,然后是调用者一个冒号:

say first <abc 2 def ghi>: Numeric ; # 2

请注意,say 是一个 sub 调用,因为下一个标记 first 后没有跟冒号。相比之下,first 是一个 method 调用,因为它后面的标记 后跟一个冒号。

脚注

1 此答案中所有关于 spaces/spacing 的评论忽略 unspacing.