语句前缀

语句前缀写在语句之前, 改变语句的意思, 语句的输出或语句运行的时刻。因为他们拥有特定的行为, 他们有时候也对某些语句或语句组有特定作用。

lazy

作为语句前缀,lazy 会在任何语句(包括 for 循环)之前起作用,从而在实际需要将其赋值给变量时保存执行。

  1. my $incremented = 0;
  2. my $var = lazy for <1 2 3 4> -> $d {
  3. $incremented++
  4. };
  5. say $incremented; # OUTPUT: «0
  6. »
  7. say eager $var; # OUTPUT: «(0 1 2 3)
  8. »
  9. say $incremented; # OUTPUT: «4
  10. »

$incremented 变量仅递增,也就是说,仅当我们热切计算包含惰性循环变量 $var 时,才运行循环的内部部分。 渴望可以通过其他方式应用于变量,例如在其上调用 .eager 方法。

  1. my @array = lazy { (^3).map( ) };
  2. say @array; # OUTPUT: «[...]»
  3. say @array.eager; # OUTPUT: «[0 1 4]
  4. »

这个前缀也可以在 gather 前面使用,以使内部语句表现得懒惰。 通常,使用此方法会使返回值的任何语句集变得懒惰。

eager

eager 语句前缀将热切地返回后面的语句的结果,从而消除惰性并返回结果。

  1. my $result := eager gather { for 1..3 { say "Hey"; take $_² } };
  2. say $result[0]; # OUTPUT: «Hey
  3. Hey
  4. Hey
  5. 1
  6. »

当与标量绑定时,gather 隐式地是惰性的。 但是,使用 eager 作为语句前缀,即使我们只是连续请求第一个,它也会在循环中运行所有三个迭代,如打印的 “Hey” 所示。

hyper, race

hyperrace 使用(可能是同时)线程在循环中运行不同的迭代:

  1. my @a = hyper for ^100_000 { .is-prime }

此代码比裸代码快3倍左右。 但是这里有一些警告:

  • 循环内的操作应花费足够的时间使线程有意义。

  • 循环内不应有对同一数据结构的读取或写入访问。 让循环产生一个结果,并分配它。

  • 如果循环中存在I / O操作,则可能存在争用,因此请避免使用。

hyperrace 之间的主要区别是结果的顺序。 如果您需要按顺序生成循环结果,请使用 hyper;如果您不关心,请使用 race

quietly

作为语句前缀,quietly 抑制其前面的语句产生的所有警告。

  1. sub marine() {};
  2. quietly say ~&marine; # OUTPUT: «marine
  3. »

在代码上调用 .Str 会产生警告。 在该语句前面加一个 quietly 只会产生输出,即例程的名称。

try

如果在语句前使用 try,它将包含其中产生的异常并将其存储在 $! 中。 变量,就像在块前使用它一样

  1. try [].pop;
  2. say $!; # OUTPUT: «Cannot pop from an empty Array
  3. ..»

do

do 可以用作语句前缀,以消除它们之前的语句的歧义; 例如,如果要分配 for 语句的结果,则需要使用此命令。 裸 for 将失败,但这将起作用:

  1. my $counter = 0;
  2. my $result = do for ^5 { $counter++ };
  3. say $counter; # OUTPUT: «5
  4. »
  5. say $result; # OUTPUT: «(0 1 2 3 4)
  6. »

在其他情况下,do 等效于用括号将语句括起来。 它可以用作(可能更多)简单语法的替代方法。

sink

与例程一样sink 将运行该语句以丢弃结果。 如果您想对其产生的副作用运行某些语句,请使用它。

  1. my $counter = 0;
  2. my $result = sink for ^5 { $counter++ };
  3. say $counter; # OUTPUT: «5
  4. »
  5. say $result; # OUTPUT: «(Any)
  6. »

once

在循环内, 仅运行带前缀的语句一次。

  1. my $counter;
  2. my $result = do for ^5 { once $counter = 0; $counter++ };
  3. say $result; # OUTPUT: «(0 1 2 3 4)
  4. »

gather

可以在语句前面使用 gather,在该语句的任何位置接收并收集从一次 take 运行发出的所有数据结构的列表:

  1. proto sub fact( Int ) {*}
  2. multi sub fact( 1 --> 1 ) {}
  3. multi sub fact( $x ) { take $x * fact( $x-1 ) }
  4. my @factors = gather say fact(13); # OUTPUT: «6227020800»
  5. say @factors;
  6. # OUTPUT: «[2 6 24 120 720 5040 40320 362880 3628800 ...]»

在此示例中,gathersay 之前,它打印阶乘的第一个结果; 同时,它从每次对事实的调用中都收集了结果,该结果发送到 @factor

start

作为语句前缀,start 的行为与在块前面的行为相同,即,它以异步方式运行该语句,并返回 promise。

  1. proto sub fact( Int ) {*}
  2. multi sub fact( 1 --> 1 ) {}
  3. multi sub fact( $x ) { $x * fact( $x-1 ) }
  4. my @promises = gather {
  5. for <3 4> {
  6. take start fact( 10 ** $_ );
  7. }
  8. }
  9. say await @promises;

start 创建的 Promises 收集在一个数组中,一旦实现了 Promise,它就会返回操作的结果。

react

react 可以在并发程序中用于创建代码块,这些代码块在某些事件发生时运行。 它适用于块,也可用作语句前缀。

  1. my Channel $KXGA .= new;
  2. for ^100 {
  3. $KXGA.send( (100000..200000).pick );
  4. }
  5. my @sums = ( start react whenever $KXGA -> $number {
  6. say "In thread ", $*THREAD.id;
  7. say "→ ", (^$number).sum;
  8. } ) for ^10;
  9. start { sleep 10; $KXGA.close(); }
  10. await @sums;

在这种情况下,react 前置于 whenever,这会使从通道中读取的每个数字都变得很长。

supply

关键字 supply 可创建您可以点击的按需供应)。 它与 emit 配对,可以在 supply 前缀语句中的任何位置使用它。

  1. my &cards = -> {
  2. my @cards = 1..10 X~ <♠ ♣>;
  3. emit($_) for @cards.pick(@cards.elems);
  4. }
  5. my $supply = supply cards;
  6. $supply.tap( -> $v { say "Drawing: $v" });
  7. $supply.tap( -> $v { say "Drawing: $v" }, done => { say "No more cards" });
  8. # OUTPUT:
  9. # [...]
  10. # Drawing: 1♥
  11. # Drawing: 7♥
  12. # Drawing: 9♥
  13. # No more cards