开关

toggle 字面意思是开关, 函数签名如下:

1
method toggle(Any:D: *@conditions where .all ~~ Callable:D, Bool :$off  --> Seq:D)

它接收一个数组, 数组中的每个元素都是一个 Callable

它迭代调用者, 产生一个 Seq, 根据开关是开还是关把接收到的值填充到结果中, 开关是开还是关取决于 @conditions 数组里面调用 Callables 的结果:

1
2
say (1..15).toggle(* < 5, * > 10, * < 15); # OUTPUT: «(1 2 3 4 11 12 13 14)␤» 
say (1..15).toggle(:off, * > 2, * < 5, * > 10, * < 15); # OUTPUT: «(3 4 11 12 13 14)␤»

想象一下开关是打开还是关闭(TrueFalse),如果它打开则产生值。默认情况下,该开关的初始状态处于 “on” 位置,除非 :$off 设置为 true 值,在这种情况下,初始状态将为”off”。

@conditions 的头部开始拿出一个 Callable(如果有的话)并且它成为当前的 tester。通过使用该值调用 tester Callable 来测试原始序列中的每个值。我们想象的开关的状态被设置为 tester 的返回值:如果它是真的,将开关设置为 “on”,否则将其设置为 “off”。

无论何时切换开关(即从”off”切换到”on”或从”on”切换到”off”),当前 tester Callable 将替换为 @conditions 中的下一个 Callable,如果可用,将用于测试进一步的值。如果没有更多可用的 tester Callables,则开关将保持其当前状态,直到迭代结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# our original sequence of elements: 
say list ^10; # OUTPUT: «(0 1 2 3 4 5 6 7 8 9)␤»
# toggled result:
say ^10 .toggle: * < 4, * %% 2, &is-prime; # OUTPUT: «(0 1 2 3 6 7)␤»

# 第一个 tester Callable 是 `* < 4` 开关的初始状态是 "on".
# 当我们迭代我们的原始序列时:
# 0 => 0 < 4 === True 开关是开着的, 值进入到结果中, 开关切换
# 所以我们继续使用同一个 Callable:
# 1 => 1 < 4 === True same
# 2 => 2 < 4 === True same
# 3 => 3 < 4 === True same
# 4 => 4 < 4 === False 开关关闭, "4" 没有进入到结果中
# 此外, 我们的开关发生切换,
# 所以, 我们切换到下一个 Callable
# 5 => 5 %% 2 === False 开关仍旧关闭, 继续尝试找到满足条件的值
# 6 => 6 %% 2 === True 开关开启, 把 "6" 放入到结果中
# 开关切换, 所以我们会使用下一个 tester Callable
# 7 => is-prime(7) === True 开关仍然开启, 将值放入结果中并继续
# 8 => is-prime(8) === False 开关现在关闭, "8" 没有被放入到结果中
# 开关切换, 但是我们再没有
# tester Callables 了,
# 所以对于剩下的序列它会一直保持关闭.

由于开关状态的切换加载下一个 tester Callable,设置 :$offTrue 值会影响第一个 tester 何时被丢弃:

1
2
3
4
5
6
7
8
9
10
11
# our original sequence of elements: 
say <0 1 2>; # OUTPUT: «(0 1 2)␤»
# toggled result:
say <0 1 2>.toggle: * > 1; # OUTPUT: «()␤»

# 第一个 tester Callable 是 `* > 1` 并且开关的初始状态是 "on".
# 当我们迭代我们的原始序列时:
# 0 => 0 > 1 === False 开关关闭, "0" 没有进入到结果中.
# 此外, 发生了切换, 所以我们要改变 tester Callable
# 但是因为我们没有 tester Callable 可用了
# 所以开关会保持 "off" 状态直到结束
1
2
3
4
5
6
7
8
9
10
11
12
# our original sequence of elements: 
say <0 1 2>; # OUTPUT: «(0 1 2)␤»
# toggled result:
say <0 1 2>.toggle: :off, * > 1; # OUTPUT: «(2)␤»

# 第一个 tester Callable 是 `* > 1` 并且开关的初始状态是 "off".
# 当我们迭代我们的原始序列时:
# 0 => 0 > 1 === False 开关关闭, "0" 没有进入到结果中
# 这一次开关没有发生切换
# 所以我们继续使用我们当前的 tester Callable
# 1 => 1 > 1 === False same
# 2 => 2 > 1 === True 开关开启, "2" 进入到结果中

一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
my $excerpt = q:to/END/;
Here's some unimportant text.
=begin code
This code block is what we're after.
We'll use 'ff' to get it.
=end code
More unimportant text.
=begin code
I want this line.
and this line as well.
HaHa
=end code
More unimport text.
=begin code
Let's to go home.
=end code
END


.say for $excerpt.lines
.toggle( * ~~ /'=begin code'/,
* ~~ /'=end code'/)

输出:

1
2
3
4
5
6
7
8
9
10
=end codeMore unimportant text.
=begin code
I want this line.
and this line as well.
HaHa
=end code
More unimport text.
=begin code
Let's to go home.
=end code