可以通过几种方式调用函数:
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 的回答,更详尽地介绍例行调用语法的精确细节。(作为一个重要的例子,如果例程名称和冒号 (:
) 或左括号 ((
) 之间有任何空格,这些调用的含义通常会改变)。