语句§
Raku 程序由一个或多个语句组成。简单语句用分号隔开。以下程序将在下一行打印“Hello”,然后打印“World”。
say "Hello";say "World";
在语句中出现空格的大多数地方,以及分号之前,它们可以拆分为多行。此外,同一行上可以出现多个语句。这会很笨拙,但上面的示例也可以写成
say"Hello"; say "World";
块§
与许多其他语言一样,Raku 使用 {
和 }
括起来的 块
将一系列语句转换为充当单个语句的 Block
。在块中的最后一个语句和结束 }
之间省略分号是可以的。
当一个代码块作为单独的语句存在时,它将在前一个语句执行完后立即进入,并且其中的语句将被执行。
say 1; # OUTPUT: «1»; # OUTPUT: «23»say 4; # OUTPUT: «4»
除非它作为单独的语句存在,否则代码块只是创建一个闭包。其中的语句不会立即执行。闭包是另一个主题,它们的使用方式在其他地方有解释。现在重要的是要理解代码块何时运行以及何时不运行。
say "We get here";;or die;
在上面的例子中,在执行第一个语句之后,第一个代码块作为第二个语句单独存在,所以我们执行它里面的语句。第二个代码块是一个闭包,所以它创建了一个类型为Block
的对象,但不会执行它。对象实例通常被认为是真值,所以代码不会停止,即使该代码块如果被执行的话会计算为 0。这个例子没有说明如何处理Block
对象,所以它就被丢弃了。
下面介绍的大多数流程控制结构只是告诉 Raku 何时、如何以及执行多少次像第二个代码块这样的代码块。
在我们深入讨论这些内容之前,关于语法的另一个重要说明:如果在闭合花括号后面的行上没有内容(或只有注释),而你通常会在那里放置分号,那么你不需要分号。
# All three of these lines can appear as a group, as is, in a program# OUTPUT: «42»# OUTPUT: «43»; # OUTPUT: «4243»
...但是
# Syntax error# Also a syntax error, of course
所以,当你使用换行编辑器进行退格操作时要小心。
\# Syntax error
在大多数语言中,你必须注意这一点,以防止意外地将内容注释掉。下面许多例子可能包含不必要的分号,以提高清晰度。
类体对于任何顶层表达式都像简单的代码块一样;角色和其他包也是如此,比如语法(实际上是类)或模块。
;my = C.new; │# OUTPUT: Fails and writes «I liveI will never live!
这个代码块将首先执行第一个语句,然后die
打印第二个语句。$c
永远不会获得值。
Phasers§
代码块可能具有phasers:特殊的标记代码块,它们将代码块的执行分成阶段,这些阶段在特定阶段运行。有关详细信息,请参阅phasers页面。
do§
在代码块不能作为单独语句存在的情况下,运行它的最简单方法是在它之前写do
。
# This dies half of the timedo or die; say "I win.";
请注意,do
和代码块之间需要一个空格。
整个do {...}
计算为代码块的最终值。当需要该值来计算表达式其余部分时,将运行该代码块。所以
False and do ;
...不会显示 42。但是,代码块在包含它的表达式每次被计算时只被计算一次。
# This says "(..1 ..2 ..3)" not "(..1 ...2 ....3)"my = "."; say do X~ 1, 2, 3;
换句话说,它遵循与其他所有内容相同的reification规则。
从技术上讲,do
是一个循环,它只运行一次迭代。
do
也可以用于裸语句(没有花括号),但这主要只是为了避免在语句是表达式中的最后一项时需要使用括号。
3, do if 1 ; # OUTPUT: «(3, 2)»3, (if 1 ) ; # OUTPUT: «(3, 2)»
3, if 1 ; # Syntax error
start§
以异步方式运行语句或代码块的最简单方法是在它之前写start
。
startsay "working";# working, done
请注意,start
和代码块之间需要一个空格。在上面的例子中,start
代码块处于接收器上下文中,因为它没有被分配给变量。从版本 6.d 开始,这些接收器代码块有一个异常处理程序附加。
startsay "working";sleep 10;
这段代码将在版本 6.d 中打印Unhandled exception in code scheduled on thread 4 We're dead
,而在版本 6.c 中,它将在等待 10 秒后简单地退出。
start {...}
立即返回一个Promise
,如果你对代码块的结果不感兴趣,可以安全地忽略它。如果你对代码块的最终值感兴趣,可以调用返回的 promise 的.result
方法。所以
my = start# ... do other stuffsay "The result is $promise.result()";
如果代码块中的代码还没有完成,对.result
的调用将等待它完成。
当异步执行的唯一操作是子例程或方法时,start
用于裸语句很有用。
sub get42my = start get42;say .result; # OUTPUT: «42»
请注意,启动代码无法访问其外部块的特殊变量 $!
和 $/
,但会收到新的特殊变量,因此每个异步任务都有其特定于任务的状态。
因此,在异步任务中执行的 try
表达式和正则表达式匹配具有其特定于任务的状态。
'a' ~~ /a/; # $/ is set to 「a」try die; # $! is defined now with an anonymous AdHoc exception# as a code blockawait start ; # OUTPUT: «Nil»await start ; # OUTPUT: «Nil»# as a single statementawait start $!.say; # OUTPUT: «Nil»await start $/.say; # OUTPUT: «Nil»
if§
要条件性地运行一段代码,请使用 if
后跟一个条件。该条件是一个表达式,将在 if
语句之前的语句完成之后立即进行评估。只有当该条件在强制转换为 Bool
时表示 True
时,才会评估附加到该条件的代码块。与某些语言不同,该条件不需要加括号,而是代码块周围的 {
和 }
是必需的。
if 1 ; # says "1 is true"
if 1 "1 is true".say ; # syntax error, missing block
if 0 ; # does not say anything, because 0 is false
if 42.say and 0 ; # says "42" but does not say "43"
还有一种称为“语句修饰符”形式的 if
。在这种情况下,if
和条件位于要条件性运行的代码之后。请注意,条件始终首先进行评估。
43.say if 42.say and 0; # says "42" but does not say "43"43.say if 42.say and 1; # says "42" and then says "43"say "It is easier to read code when 'if's are kept on left of screen"if True; # says the above, because it is trueif True; # says "43" as well
语句修饰符形式可能最好谨慎使用。
if
语句本身将要么 Slip
给我们一个空列表(如果它没有运行代码块),要么返回代码块产生的值。
my = 0; say (1, (if 0 ), 3, ); # says "(1 3 0)"my = 0; say (1, (if 1 ), 3, ); # says "(1 2 3 42)"say (1, (if 1 ), 3); # does not slip, says "(1 (2 2) 3)"
对于语句修饰符,情况相同,只是您拥有语句的值而不是代码块。
say (1, (42 if True) , 2); # says "(1 42 2)"say (1, (42 if False), 2); # says "(1 2)"say (1, 42 if False , 2); # says "(1 42)" because "if False, 2" is true
if
默认情况下不会更改主题 ($_
)。为了访问条件表达式产生的值,您必须更强烈地请求它。
= 1; if 42 ; # says "1"= 1; if 42 -> ; # says "42"= 1; if 42 -> ; # says "1" then says "42"= 1; if 42 ; # says "1" then says "42"
else/elsif
§
可以通过在 if
条件之后添加 else
来生成一个复合条件,以提供一个备用代码块,在条件表达式为假时运行。
if 0 else ; # says "yes"if 0 else ; # says "yes", space is not required
else
不能用分号与条件语句分开,但作为特殊情况,可以使用换行符。
if 0 ; else ; # syntax error
if 0else ; # says "yes"
可以使用 elsif
将其他条件夹在 if
和 else
之间。只有在它之前的所有条件都为假时,才会评估额外的条件,并且只有第一个真条件旁边的代码块才会运行。如果需要,可以使用 elsif
而不是 else
。
if 0 elsif False else # says "yes"if 0 elsif True else # says "YES"if 0 elsif False # does not say anythingsub rightsub wrongif wrong() elsif right() else# The above says "Wrong!" then says "Right!" then says "yes"
您不能将语句修饰符形式与 else
或 elsif
一起使用。
42.say if 0 else # syntax error
所有关于分号和换行符的相同规则始终适用。
if 0 ; elsif 1 else ; # syntax errorif 0 elsif 1 ; else ; # syntax error
if 0 elsif 1 else ; # says "1"
if 0 elsif 1else ; # says "1"if 0elsif 1 else ; # says "1"if 0elsif Falseelse ; # says "yes"
整个内容要么 Slip
给我们一个空列表(如果没有任何代码块运行),要么返回运行的代码块产生的值。
my = 0; say (1,(if 0 elsif False ),3, ); # says "(1 3 0)"my = 0; say (1,(if 0 else ),3, ); # says "(1 2 3 43)"
可以在 else
中获取先前表达式的值,该值可以来自 if
或最后一个 elsif
(如果有)。
= 1; if 0 else -> ; # says "1 0"= 1; if False else -> ; # says "1 False"if False elsif 0 else -> ; # says "0"
unless
§
当您厌倦了键入“if not (X)”时,可以使用 unless
来反转条件语句的意义。您不能将 else
或 elsif
与 unless
一起使用,因为这会导致混淆。除了这两个区别之外,unless
的工作方式与 if 相同。
unless 1 ; # does not say anything, since 1 is true
unless 1 "1 is false".say ; # syntax error, missing block
unless 0 ; # says "0 is false"
unless 42.say and 1 ; # says "42" but does not say "43"43.say unless 42.say and 0; # says "42" and then says "43"43.say unless 42.say and 1; # says "42" but does not say "43"= 1; unless 0 ; # says "1"= 1; unless 0 -> ; # says "0"= 1; unless False -> ; # says "False"my = 0; say (1, (unless 0 ), 3, ); # says "(1 2 3 42)"my = 0; say (1, (unless 1 ), 3, ); # says "(1 3 0)"
with orwith without
§
with
语句类似于 if
,但它测试的是定义性而不是真值,并且它在条件上进行主题化,就像 given
一样。
with "abc".index("a") # prints 0
与 elsif
类似,orwith
可用于链接定义性测试。
# The below code says "Found a at 0"my = "abc";with .index("a")orwith .index("b")orwith .index("c")else
您可以混合使用基于 if
的子句和基于 with
的子句。
# This says "Yes"if 0 orwith Nil orwith 0 ;
与 unless
一样,您可以使用 without
来检查未定义性,但不能添加 else
子句。
my = Any;without
还有 with
和 without
语句修饰符。
my = (Any, True).roll;say 42 with ;warn "undefined answer" without ;
与其他可链接结构一样,完成 with/if
..orwith/elsif
链的 else
本身将主题化为先前(失败)条件的主题的值(无论是 with
的主题还是最后一个 orwith
或 elsif
)。
在 else
紧随 with
或 orwith
之后的情况下,主题化为一个保证未定义的值可能看起来毫无用处。但当与可能失败的操作一起使用时,它会形成一个有用的习惯用法,因为 Failure
值始终未定义。
sub may_fail( --> Numeric )with may_fail() ->else
请注意,虽然将 Failure
作为主题会将其标记为 handled
(因此您可以使用 with
/else
安全地继续执行),但这并不会使Failure 值本身安全。即使在 else
子句中,如果您尝试直接使用该值,也会导致 else
子句本身失败(或者,在 Rakudo 中,将 Failure 提升为抛出的异常)。
但如上所述,您可以在 else
主题化的已处理 Failure
对象中使用方法,例如 exception
,如果您希望提供诊断信息或查询底层的 Exception
。
when§
when
块类似于 if
块,两者都可以在外部块中使用;它们也有“语句修饰符”形式。但它们在处理同一外部块中的后续代码方面有所不同:当执行 when
块时,控制权将传递给封闭块,后续语句将被忽略;但当执行 if
块时,后续语句将被执行。[1] 以下示例将说明 if
或 when
块的默认行为,假设 if
或 when
块中不包含任何特殊的退出或其他副作用语句。
如果上面的 if
和 when
块出现在文件范围内,则在每种情况下都会执行后续语句。
when
还有一个 if
没有的功能:when
的布尔上下文测试默认值为 $_ ~~
,而 if
的则没有。这会影响如何在没有 $_
值的情况下在 when
块中使用 X(在这种情况下它是 Any
,并且 Any
与 True
智能匹配:Any ~~ True
会产生 True
)。考虑以下情况
最后,when
的语句修饰符形式不会影响在另一个块内或外部执行后续语句。
say "foo" when X; # if X is true statement is executed# following statements are not affected
由于成功匹配将退出块,因此这段代码的行为
= True;my ;;say ; # OUTPUT: «(Any)»
在任何值被存储或处理之前就放弃了 do
块,因此可以解释。但是,在这种情况下
= False;my ;;say ; # OUTPUT: «False»
由于比较为假,因此不会放弃该块,所以 $a
实际上会获得一个值。
for§
for
循环遍历列表,在每次迭代时运行 Block
内的语句。如果该块接受参数,则列表的元素将作为参数提供。默认情况下,该块接受一个参数,$_
my = 1..3;for # prints each value contained in @foofor # same thing, because .print implies a $_ argumentfor # prints 42 as many times as @foo has elements
可以使用尖括号块语法或 占位符 来命名参数
my = 1..3;for ->for # same thing
可以声明多个参数,在这种情况下,迭代器将从列表中获取所需数量的元素,然后运行该块。
my = 1..3;for .kv -> ,my = <a b c> Z=> 1,2,3;for .kv -> ,for 1, 1.1, 2, 2.1 # OUTPUT: «1 < 1.12 < 2.1»
尖括号块的参数可以具有默认值,允许代码处理缺少元素的列表。
my = 1,2,3,4;for -> , = 'N/A', = 'N/A'# OUTPUT: «1 2 34 N/A N/A»
当没有为 for
循环的块指定参数时,可以在其中使用 when
,类似于在 given
块中使用它。
# A solution for FizzBuzz:for 1..100
如果使用 for
的后缀形式,则不需要块,主题将被设置为语句列表。
say „I $_ butterflies!“ for <♥ ♥ ♥>;# OUTPUT: «I ♥ butterflies!I ♥ butterflies!I ♥ butterflies!»
for
可以用于延迟列表 - 它只会在需要时从列表中获取元素,因此要逐行读取文件,可以使用
for .lines ->
迭代变量始终是词法变量,因此您不需要使用 my
来为它们提供适当的范围。此外,它们是只读别名。如果您需要它们可写,请使用 <->
而不是 ->
。或者,您可以添加 is rw
特性;这将执行绑定操作,因此为参数赋值会更改调用方侧的变量值。如果您想修改块内的参数副本,请添加 is copy
。
my = 1..3;for <->say ; # OUTPUT: «[Odd Even Odd]»= 1..3;for -> is rwsay ; # OUTPUT: «[Odd Even Odd]»= 1..3;my ;for -> is copysay ; # OUTPUT: «[1 2 3]»say ; # OUTPUT: «[Odd Even Odd]»
此规则也适用于主题变量 $_
,默认情况下它是一个读写别名;如果它在 ->
循环中使用,它将变为只读。
my = 1..3;for -># Error: ...require mutable argumentsfor ->
for
循环可以生成一个由每次运行附加块产生的值组成的 List
。要捕获这些值,请将 for
循环放在括号中或将它们分配给数组
(for 1, 2, 3 ).say; # OUTPUT: «(2 4 6)»my = do for 1, 2, 3 ; .say; # OUTPUT: «[2 4 6]»my = (for 1, 2, 3 ); .say; # OUTPUT: «[2 4 6]»
这意味着,如果循环的结果没有被分配,它们将处于 sink 上下文 中
Sunk.new( :titanic() ) for ^3;for 1# OUTPUT:# Sinking 0# Sinking 1# Sinking 2# About to sink# Sinking 1
第一个循环创建了三个元素,但它们处于 sink 上下文,因此调用了它的 `sink` 方法。在第二个循环中,它的最后一个语句将处于 sink 上下文,因此它也将被 sink(从版本 6.d 开始)。
常量 `Empty` 将充当循环的无操作。
say "Not here" for Empty;
不会做任何事情。此常量 等效于一个空的 Slip 或 List。
未定义的值将以相同的方式运行。
my := Empty;.say for ;say ; # OUTPUT: «()»
将 `Empty` 赋值将有效地取消定义一个 `Array`,使用 `for` 遍历一个未定义的数组甚至不会进入循环,如所示,有效地与直接使用 `Empty` 时相同。
使用 `hyper` 和 `race`,`for` 循环可能并行迭代。另请参阅类 `Map` 中 `hyper` 和 `race` 的文档。
my = hyper for ^10_000 -> ;say .elems; # OUTPUT: «1229»say .tail: 5; # OUTPUT: «(9931 9941 9949 9967 9973)»
使用 `hyper` 保留元素的顺序。
my = race for ^10_000 -> ;say .elems; # OUTPUT: «1229»
与 `hyper` 不同,`race` 不保留元素的顺序。
gather/take§
`gather` 是一个语句或块前缀,它返回一个 序列 的值。这些值来自在 `gather` 代码的动态作用域中对 take 的调用。在下面的示例中,我们使用 `gather` 实现了一个子例程来计算一个整数的因子(注意,因子不是按顺序生成的)
sub factors( Int \n )say factors(36); # OUTPUT: «1, 36, 2, 18, 3, 12, 4, 9, 6»
`gather/take` 组合可以根据上下文延迟生成值。绑定到标量或无符号容器将强制延迟。如果您想强制延迟求值,请使用 lazy 子例程或方法。例如
my = lazy gathersay [0];say 'between consumption of two values';say [1];# OUTPUT:# 1# between consumption of two values# Produced a value# 2
`gather/take` 的作用域是动态的,因此您可以从在 `gather` 内部调用的子例程或方法中调用 `take`
sub weird(, : = 'forward')say weird(<a b c>, :direction<backward> ); # OUTPUT: «(c b a)»
如果值需要在调用方侧可变,请使用 take-rw。
请注意,由 `gather/take` 创建的 `Seq` 可能会被强制转换为另一种类型。一个分配给哈希的示例
my = gather ;say ; # OUTPUT: «{bar => 2, foo => 1}»
注意:`gather/take` 不能用于收集来自 `react/whenever` 的结果。`whenever` 块不是从运行 `gather/react` 的线程运行的,而是从运行 `emit` 的线程运行的。在这个线程上,没有处理程序来处理由 `take` 抛出的控制异常,导致它出错。
supply/emit§
关键字 `supply` 创建一个 Supply 对象,它是一个 按需供应,您可以从中获取。它与 `emit` 配对,`emit` 可以用在 `supply` 前缀代码中的任何地方。
使用 `emit 方法` 或 `emit 例程` 将调用者传递给封闭的 供应
my = supply.tap:# OUTPUT:# received Str (foo)# received Int (42)# received Rat (0.5)
另请参阅:`tap` 和 `Supplier`。
given§
`given` 语句是 Raku 的主题化关键字,类似于 C 等语言中的 `switch` 主题化。换句话说,`given` 在以下块中设置 `$_`。各个 case 的关键字是 `when` 和 `default`。通常的习惯用法如下所示
my = (Any, 21, any <answer lie>).pick;given
`given` 语句通常单独使用
given 42
这比以下代码更容易理解
(42)
default and when§
包含 `default` 语句的块将在 `default` 语句后的子块退出时立即退出。就好像块中的其余语句都被跳过了。
given 42# The above block evaluates to 43
`when` 语句也会这样做(但 `when` 语句修饰符不会)。
此外,`when` 语句对主题(`$_`)进行 `智能匹配`,以匹配提供的表达式,这样就可以在指定匹配时检查值、正则表达式和类型。
for 42, 43, "foo", 44, "bar"# OUTPUT: «4243Not an Int or a Bar44Bar»
在这个形式中,given
/when
结构的行为类似于一组 if
/elsif
/else
语句。注意 when
语句的顺序。以下代码输出 "Int"
而不是 42
。
given 42# OUTPUT: «Int»
当 when
语句或 default
语句导致外部块返回时,嵌套的 when
或 default
块不计入外部块,因此您可以嵌套这些语句并仍然处于同一个“switch”中,只要您不打开新的块。
given 42# OUTPUT: «42»
when
语句可以与 签名 进行智能匹配。
继续§
proceed
和 succeed
都只应该在 when
或 default
块中使用。
proceed
语句将立即离开 when
或 default
块,跳过其余语句,并在块之后恢复。这将阻止 when
或 default
退出外部块。
given *"This says".say;
这最常用于进入多个 when
块。proceed
将在成功匹配后恢复匹配,如下所示
given 42# OUTPUT: «Int»# OUTPUT: «42»
请注意,when 40..*
匹配没有发生。为了匹配这种情况,需要在 when 42
块中使用 proceed
。
这不像 C
的 switch
语句,因为 proceed
并不仅仅进入直接跟随的块,它会尝试再次匹配 given
值,考虑以下代码
given 42# OUTPUT: «Int»# OUTPUT: «42»
...它匹配 Int
,跳过 43
因为值不匹配,匹配 42
因为这是下一个正匹配,但不会进入 default
块,因为 when 42
块不包含 proceed
。
相比之下,succeed
关键字会短路执行并退出整个 given
块。它还可以接受一个参数来指定块的最终值。
given 42# OUTPUT: «Int»
如果您不在 when
或 default
块中,尝试使用 proceed
或 succeed
将会报错。另外请记住,when
语句修饰符形式不会导致任何块退出,并且任何此类语句中的 succeed
或 proceed
将应用于周围的子句(如果有)。
given 42# OUTPUT: «This saysThis says tooAnd this saysThis also says»
given 作为语句§
given
可以跟随一个语句来设置它所跟随语句的主题。
.say given "foo";# OUTPUT: «foo»printf "%s %02i.%02i.%i",<Mo Tu We Th Fr Sa Su>[.day-of-week - 1],.day,.month,.yeargiven DateTime.now;# OUTPUT: «Sa 03.06.2016»
循环§
loop
语句接受三个用 ;
分隔的括号中的语句,分别充当初始化器、条件和增量器。初始化器在第一次测试条件之前执行一次。如果初始化器涉及变量声明,则变量在循环的外部或包含作用域中声明为词法变量,以便它可以在循环语句之后的代码中使用。条件在每次迭代之前执行并强制转换为 Bool
;如果为 False
,则停止循环。增量器在每次迭代之后执行,并在再次测试条件之前执行。
loop (my = 0; < 10; ++)my = "However Long".comb; # Our very own .char routine:loop (my = 0;;)# undefined element (Any)say "The string is chars long.";
无限循环不需要括号。
loop
如果 loop
语句出现在列表中,它可以用来从每次运行附加块的结果中生成值。
(loop ( my = 0; ++ < 3;) ).say; # OUTPUT: «(2 4 6)»my = (loop ( my = 0; ++ < 3;) ); .say; # OUTPUT: «[2 4 6]»my = do loop ( my = 0; ++ < 3;) ; .say; # same thing
与 for
循环不同,不应该依赖于是否延迟生成返回值。最好使用 eager
来保证返回值可能被使用的循环实际运行。
sub heads-in-a-row
while, until§
while
语句只要其条件为真就执行块。所以
my = 1;while < 4print "\n";# OUTPUT: «123»
类似地,until
语句只要表达式为假就执行块。
my = 1;until > 3print "\n";# OUTPUT: «123»
while
或 until
的条件可以加括号,但关键字和条件的左括号之间必须有空格。
while
和 until
都可以用作语句修饰符。例如:
my = 42;-- while > 12
另请参见下面的 repeat/while
和 repeat/until
。
所有这些形式都可以像 loop
一样产生返回值。
repeat/while, repeat/until§
至少执行一次代码块,如果条件允许,则重复执行。这与 while
/until
不同,因为条件是在循环结束时评估的,即使它出现在前面。
my = -42;repeatwhile < 5;.say; # OUTPUT: «5»repeatwhile < 5;.say; # OUTPUT: «6»repeat while < 10.say; # OUTPUT: «10»repeat while < 10.say; # OUTPUT: «11»repeatuntil >= 15;.say; # OUTPUT: «15»repeatuntil >= 15;.say; # OUTPUT: «16»repeat until >= 20.say; # OUTPUT: «20»repeat until >= 20.say; # OUTPUT: «21»
所有这些形式都可以像 loop
一样产生返回值。
return§
子例程 return
将停止执行子例程或方法,运行所有相关的 阶段 并向调用者提供给定的返回值。默认返回值为 Nil
。如果提供了返回 类型约束,则将对其进行检查,除非返回值为 Nil
。如果类型检查失败,则会抛出异常 X::TypeCheck::Return
。如果通过,则会引发控制异常,并且可以使用 CONTROL 捕获。
代码块中的任何 return
都与该代码块外部词法范围中的第一个 Routine
绑定,无论嵌套深度如何。请注意,包根目录中的 return
将在运行时失败。在延迟评估的代码块(例如 map
内部)中的 return
可能会在执行代码块时发现外部词法例程已消失。在几乎所有情况下,last
都是更好的替代方案。请查看 函数文档,以获取有关如何处理和生成返回值的更多信息。
return-rw§
子例程 return
将返回值,而不是容器。它们是不可变的,如果尝试对其进行变异,会导致运行时错误。
sub s();say ++s();CATCH ;# OUTPUT: «X::Multi::NoMatch.new(dispatcher …
要返回可变容器,请使用 return-rw
。
sub s();say ++s();# OUTPUT: «42»
与 return
相同的规则适用于阶段和控制异常。
fail§
离开当前例程并返回提供的 Exception
或 Str
,将其包装在 Failure
中,在执行所有相关的 阶段 之后。如果调用者通过编译指示 use fatal;
激活了致命异常,则会抛出异常,而不是将其作为 Failure
返回。
sub f ;say f;CATCH# OUTPUT: «X::AdHoc: WELP!»
once§
以 once
为前缀的代码块或语句将被精确执行一次,即使它被放置在循环或递归例程中。
my ;loop# OUTPUT: «oncemanymanymany»
这适用于包含代码对象的每个“克隆”,因此
( xx 3).map: ; # says 42 thrice
请注意,当同一代码块的同一克隆被多个线程运行时,这不是一个线程安全的构造。还要记住,方法每个类只有一个克隆,而不是每个对象一个克隆。
LABELs§
while
、until
、loop
和 for
循环都可以接受一个标签,该标签可以用来标识它们以用于 next
、last
和 redo
。支持嵌套循环,例如
OUTAHERE: while True
标签也可以在嵌套循环中使用,以命名每个循环,例如
OUTAHERE:loop ( my = 1; True; ++ )
next§
next
命令开始循环的下一个迭代。因此,代码
my = 1, 2, 3, 4, 5;for ->
打印“1245”。
您也可以在 map
中使用 next
:上面的示例看起来像
my = 1, 2, 3, 4, 5;print .map: ->
打印“1 2 4 5”,因为当 Seq
被字符串化时,会在其条目之间添加空格。请注意,print
没有放在 map
的代码块中,因为它通常被认为是不好的做法,因为它的副作用(在本例中为 print
)。
如果存在 NEXT
阶段,它将在下一个迭代之前运行
my Int = 0;while ( < 10)# OUTPUT: «1 is odd.3 is odd.5 is odd.7 is odd.9 is odd.»
在 6.e.PREVIEW 版本(从 2021.07 Rakudo 编译器版本开始提供)中,也可以使用 `next` 语句返回值。这在 `map` 中使用时特别有用。
my = 1, 2, 3, 4, 5;print .map: ->
打印 "1 2 42 4 5"。
在 whenever 块中,`next` 会立即退出当前值的块。
react
打印 "0"、"1" 和 "4" - 从 0 到 4 的整数,跳过素数。
*从 6.d 版本开始,在收集其最后一个语句值的循环中,`next` 命令会为其运行的迭代返回 `Empty`。*
last§
`last` 命令会立即退出当前循环。
my = 1, 2, 3, 4, 5;for ->
打印 "12"。
你也可以在 `map` 中使用 `last`:上面的例子看起来像
my = 1, 2, 3, 4, 5;print .map: ->
打印 "1 2",因为当 Seq
被字符串化时,会在其条目之间添加空格。请注意,`print` 没有放在 `map` 的块内,因为它通常被认为是不好的做法,因为 `map` 的副作用(在本例中是 `print`)。
如果存在 LAST
阶段,它会在退出循环之前运行。
my Int = 1;while ( < 10)# OUTPUT: «The last number was 5.»
从 6.d 版本开始,在收集其最后一个语句值的循环中,`last` 命令会为其运行的迭代返回 `Empty`。
在 6.e.PREVIEW 版本(从 2021.07 Rakudo 编译器版本开始提供)中,也可以使用 `last` 语句返回值。这在 `map` 中使用时特别有用。
my = 1, 2, 3, 4, 5;print .map: ->
打印 "1 2 42"。
redo§
`redo` 命令会重新启动循环块,而不会再次评估条件。
for 1..5 -># OUTPUT: «Entering #1... Entering #2... Entering #3... Entering #3... Entering #4... Entering #5... Entering #5... »