在乐语中简洁地打印数学系列



数学系列,以这里表示为数组的连续序列为例:

my @seq = my $a=0, {++$a} ... *;
for @seq[^10].kv {state $f=0; ($^k < 4 or $^k > 7) ?? say "a$^k =  "  ~ $^v !! (say "..." if $f ne 1; $f=1) };

指纹:

a0 =  0
a1 =  1
a2 =  2
...
a8 =  8
a9 =  9

1-有没有一种简单的方法可以只删除第一个元素,即a0 = 0打印输出?

2-此代码可以更惯用吗?

这可能更习惯一些:

my @seq = 0, *+1 ... *;
say @seq[^4], @seq[7..10]

不需要在序列中使用词法变量;Whatever变量或占位符变量都可以安全地在序列中使用。然后,您可以简单地选择要打印的序列元素。 返回«(0 1 2 3)(7 8 9 10)␤»

您可以使用skip跳过任何IterableSequence的前 N 个值:

for (^5).skip(3) {
.say
}
# 3
# 4

如果不指定数字,它将仅跳过一个元素。

准系统解决方案

让我们从一个非常简单的解决方案开始,用于打印序列的要点。它不涉及您添加到问题中的细节,但这是一个很好的起点:

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 个元素的小列表的惯用方法(前提是它不懒惰;在 mo 中有更多关于这一点的信息)。

给定此初始解决方案,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 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例程参数/参数并调用它而不是内置的.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-gistsub 主体中的"方法"调用现在是如何.&gist的,而不是.gist的。语法.&foo调用&foo(通常通过编写foo调用),将.左侧的调用作为$_参数传递给 sub。

另请注意,我已经通过在&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)

接下来,如果潜艇在没有范围或要点的情况下调用,那就太好了。我们主要可以通过为@range&gist参数提供合适的默认值来解决这个问题:

sub seq-range-gist (
@seq,
@range = @seq.is-lazy ?? ^100 !! ^@seq,
:&gist = { .gist }
) {

如果@seq不是懒惰的,则@range默认为全范围@seq。如果@seq无限的(在这种情况下它也是懒惰的),那么最多 100的默认值就可以了。但是,如果@seq是懒惰的,但产生的定义值少于 100 个怎么办?为了涵盖这种情况,我们在@pairs声明中附加了.grep: *.value.defined

my @pairs = @seq.pairs[@range].grep: *.value.defined;

另一个简单的改进是可选的头部和尾部参数,从而产生最终的抛光解决方案:

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)
}
my @seq = my $a=0, {++$a} ... *;
my i = 0;
say( 'a' ~ (i+$_) Z=> (i+$_) ) for @seq[^5]; 
print "------n";
my j = 1;
say( 'a'.succ ~ (j+$_) Z=> (j+$_) ) for @seq[^5]; 

输出:

(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
------
(b1 => 1)
(b2 => 2)
(b3 => 3)
(b4 => 4)
(b5 => 5)

我承认以上不包括您的"定制"gist省略号条件(($^k < 4 or $^k > 7)),但您似乎在评论中提出了一种更优雅的写作方式。仍然(如果您不想使用skip),自己对键进行数字运算,并包括一个偏移量,例如ij,表示您希望跳过多少个@seq元素。

附录:以下是实现"定制"gist省略号条件(使用grep)的尝试:

my @seq = my $a=0, {++$a} ... *;
my i = 0; my m = 4; my n = 7;
do for @seq[^10].grep({4 > $_ or $_ > 7 }) {
say 'a' ~ (i+$_) Z=> (i+$_); 
if  $_ == 3 {print "...n"};
}

i = 0时的输出:

(a0 => 0)
(a1 => 1)
(a2 => 2)
(a3 => 3)
...
(a8 => 8)
(a9 => 9)

i = 1时的输出:

(a1 => 1)
(a2 => 2)
(a3 => 3)
(a4 => 4)
...
(a9 => 9)
(a10 => 10)

最新更新