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



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

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

后者似乎通过了Positional,但除此之外,我不知道它们还有什么不同。是否有任何需要了解的重要差异?你会使用哪种类型的情况?

@jjmerelo的答案涵盖了基础知识。这个补充答案旨在有些详尽,但希望不会令人筋疲力尽,涵盖了陷阱、罕见案例和建议。

foo: valuea, valueb, ...

也许令人惊讶的是,这不是对名为foo的子或方法的调用。

相反,它是一个以标签开头的语句,foo:.

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

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

"无用使用"警告意味着<a b c>不会以有用的方式使用。say:没有对值列表执行任何操作。它只是一个不做任何事情的标签。

大概你正在使用类似Perl 6 REPL的东西。如果未以其他方式使用,REPL 会自动say行中的最后一个值,从而使该行看起来没有警告即可工作。

.a-method:

如果使用表单.a-method后缀方法调用除了 invocant (.左侧的参数或当前主题(如果没有显式调用)之外没有其他参数,那么您可以将其编写为:

42.say ;

您可以选择附加冒号:

42.say: ;

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

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

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

一种方法是在方法名称之后,在参数之前立即写一个冒号。方法名称和冒号之间不能有空格,方法参数之前的冒号后面必须有空格。1

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

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

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

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

冒号表单方法调用的参数列表也由有效的语句修饰符结束,如given

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

a-sub arg1, arg2, ...

这是子例程调用的相应形式。唯一的格式差异是 sub 在子名称之前没有调用或.您必须省略子名称后的冒号。

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

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

用于方法和 sub 调用的另一种常见形式是紧跟在方法或子名称后面,并带有括号来分隔参数。开场白必须紧跟在后面,例程名称和(之间没有任何空格。

以下是与.first方法一起使用的括号:

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

这样做的好处是,它可以说比使用外部参数的替代方案更漂亮:

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

如果要将 sub调用直接放在双引号字符串中,则需要在 sub name 前面加上&符号并使用后缀 parens 形式:

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后不跟冒号,因此方法调用被视为完成。

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

( arrgh, arrgh, ... )这些都不是。因此,您会收到"连续两个术语"编译错误。

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

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

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

这样做在冒号和开头参数之间没有空格会产生一个神秘的编译错误:

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

离开一个空间似乎有效 - 但通常只能靠运气:

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方法对作为形式列表的单个参数没有做任何有用的事情(Numeric, :k).

当然,您可能偶尔想要传递一个参数,该参数是 paren 中的值列表。但是您可以在不使用冒号的情况下执行此操作。为了清楚起见,我的建议是你把它写成:

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

有一个调用表单,它允许您调用声明为 sub 的例程 - 但使用.method调用语法。下面将点左侧的"invocant"qux作为第一个参数提供给名为a-sub的 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调用,因为下一个标记first后跟不跟冒号。相比之下,first是一个方法调用,因为标记后面着冒号。

脚注

1此答案中所有关于空格/间距的评论都忽略了取消间距。

正如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

这些句子都将返回相同的(False False False True False)

一般来说,正如你在上面看到的map,你可以在任何使用:的方法调用中使用(),但相反的情况并非如此;:只能在方法调用中使用。

如果需要精确分隔参数,请使用(),正如 Raiph 在下面评论的那样。

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

最新更新