准系统解决方案
让我们从一个非常简单的打印序列要点的解决方案开始。它不涉及您添加到问题中的细节,但它是一个很好的起点:
sub seq-range-gist ( @seq ) {
my @pairs = @seq.pairs;
join "\n", @pairs.head(3)».gist, '...', @pairs.tail(2)».gist
}
与.kv 不同,它将其调用者转换为key1, value1, key2, value2, key3, value3, ... 的形式,即如果其调用者包含3 个元素,则为6 个元素,.pairs 将其调用者转换为key1 => value1, key2 => value2, key3 => value3, ... 的形式。
我使用.pairs 而不是.kv 部分是因为这意味着我可以稍后在代码中使用».gist 轻松地为每个元素获得漂亮的key1 => value1 显示。我们将在下面对其进行修改,但这是一个很好的惯用开始。
.head 和 .tail 调用是从调用者列表中创建包含第一个和最后 N 个元素的小列表的惯用方法(前提是它不是懒惰的;稍后会详细介绍)。
鉴于此初始解决方案,say seq-range-gist (0,1 ... Inf)[^10] 显示:
0 => 0
1 => 1
2 => 2
...
8 => 8
9 => 9
接下来,我们希望能够“仅从打印输出中删除第一个元素……”。不幸的是 say seq-range-gist (0,1 ... Inf)[1..9] 显示:
0 => 1
1 => 2
2 => 3
...
7 => 8
8 => 9
我们希望=> 左侧的编号保留原始序列的编号。为了实现这一点,我们将基础序列从我们想要提取的范围中分离出来。我们添加第二个参数/参数@range,并将[@range] 附加到sub 的第二行:
sub seq-range-gist ( @seq, @range ) {
my @pairs = @seq.pairs[@range];
现在我们可以写say seq-range-gist (0,1 ... Inf), 1..9来显示:
1 => 1
2 => 2
3 => 3
...
8 => 8
9 => 9
在您的问题中,您使用了aINDEX = VALUE 而不是INDEX => VALUE 格式。为了允许自定义要点,我们添加了第三个 &gist 例程参数/参数并调用它而不是内置的 .gist 方法:
sub seq-range-gist ( @seq, @range, :&gist ) {
my @pairs = @seq.pairs[@range];
join "\n", @pairs.head(3)».&gist, '...', @pairs.tail(2)».&gist
}
注意seq-range-gist sub 主体中的“方法”调用现在是.&gist,而不是.gist。语法.&foo 调用sub &foo(通常只写foo 来调用),将. 左侧的调用者作为$_ 参数传递给子。
另请注意,我已将 &gist 参数命名为在其前面加上 :。
所以现在say seq-range-gist (0,1 ... Inf), 1..9, gist => { "a{.key} = {.value}" } 显示:
a1 = 1
a2 = 2
a3 = 3
...
a8 = 8
a9 = 9
添加润色
这个答案的其余部分是为关心波兰语的读者提供的奖励材料。
say seq-range-gist (0, 1, 2, 3), ^3 显示:
0 => 0
1 => 1
2 => 2
...
1 => 1
2 => 2
哎呀。即使有比头部和尾部组合更多的对,所以至少我们没有得到重复的行,使用head, ..., tail 方法来省略一两个元素仍然没有意义。让我们更改子正文中的最后一条语句以消除这些问题:
join "\n",
@pairs < $head + $tail + 3 # Of course, the 3 is a bit arbitrary
?? @pairs».&gist
!! (@pairs.head($head)».&gist, '...', @pairs.tail($tail)».&gist)
接下来,如果在没有范围或要点的情况下调用 sub 做一些有用的事情,那就太好了。我们可以通过给@range 和&gist 参数提供合适的默认值来解决这个问题:
sub seq-range-gist (
@seq,
@range = @seq.is-lazy ?? ^100 !! ^@seq,
:&gist = { .gist }
) {
如果@seq 不是 lazy,则@range 默认为@seq 的完整范围。如果@seq 是 infinite(在这种情况下它也是惰性的),那么 最多 100 的默认值就可以了。但是如果@seq 是惰性的但产生的定义值少于 100 个呢?为了解决这种情况,我们将.grep: *.value.defined 附加到@pairs 声明中:
my @pairs = @seq.pairs[@range].grep: *.value.defined;
另一个简单的改进是可选的 head 和 tail 参数,最终得到完善的解决方案:
sub seq-range-gist (
@seq,
@range = @seq.is-lazy ?? ^100 !! ^@seq,
:$head = 3,
:$tail = 2,
:&gist = { .gist }
) {
my @pairs = @seq.pairs[@range].grep: *.value.defined;
join "\n",
@pairs <= $head + $tail + 2
?? @pairs».&gist
!! (@pairs.head($head)».&gist, '...', @pairs.tail($tail)».&gist)
}