【问题标题】:Printing mathematical series concisely in Raku在 Raku 中简洁地打印数学系列
【发布时间】:2022-01-12 02:48:37
【问题描述】:

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

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- 这段代码可以更惯用吗?

【问题讨论】:

  • @DanBron 感谢您的评论。我刚刚对原帖进行了编辑和阐述。

标签: sequence raku


【解决方案1】:

这可能更惯用:

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

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

【讨论】:

  • 感谢您的回答。 whateveroperator 令人耳目一新,但系列/序列输出并未解决主要问题。我想打印在数学教科书上看到的系列,即在两者之间使用... 符号。
【解决方案2】:

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

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

如果不指定数字,则只会跳过一个元素。

【讨论】:

  • skip 似乎只删除了输出,即保留第 0 个索引 (a0) 的元素。我试过@seq:delete,它只是用(Any)替换了第0个元素
  • 确实如此。 skip 就像跳过的元素不存在一样。这可能是也可能不是你想要的:-)
  • 当我将skip 放在两者之间时,它会显示为:for @seq[^10].skip(0).kv字面上不会跳过第 0 个元素,如果我给出也没关系作为skip1 或2 的参数,它只会进一步扭曲输出。我需要一种实用的方法来从头开始删除第 0 个元素。
  • 也许for @seq[^10].kv.skip(2) 就是您要找的东西?
  • 对于第二个问题:而不是 ` ($^k 7)` ($^k == (4..7).none) 看起来不是那么简洁,而是更惯用。
【解决方案3】:

准系统解决方案

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

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 =&gt; value1, key2 =&gt; value2, key3 =&gt; value3, ... 的形式。

我使用.pairs 而不是.kv 部分是因为这意味着我可以稍后在代码中使用».gist 轻松地为每个元素获得漂亮的key1 =&gt; 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

我们希望=&gt; 左侧的编号保留原始序列的编号。为了实现这一点,我们将基础序列从我们想要提取的范围中分离出来。我们添加第二个参数/参数@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 =&gt; VALUE 格式。为了允许自定义要点,我们添加了第三个 &amp;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 主体中的“方法”调用现在是.&amp;gist,而不是.gist。语法.&amp;foo 调用sub &amp;foo(通常只写foo 来调用),将. 左侧的调用者作为$_ 参数传递给子。

另请注意,我已将 &amp;gist 参数命名为在其前面加上 :

所以现在say seq-range-gist (0,1 ... Inf), 1..9, gist =&gt; { "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&amp;gist 参数提供合适的默认值来解决这个问题:

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

如果@seq 不是 lazy,则@range 默认为@seq 的完整范围。如果@seqinfinite(在这种情况下它也是惰性的),那么 最多 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)
}

【讨论】:

  • 最小的解决方案工作得很好,而且也很地道。在我的解决方案中,我不得不求助于“标志”变量来计算... 部分,导致它看起来更像一个 C 程序。所以这真的回答了我问题的两个部分。至于“全面”的解决方案,看起来有点吓人,真的。
  • 感谢您的反馈并接受我的回答@LarsMalmsteen。也就是说,我已经完全重写了我的答案,感觉好多了。我已经放弃了“综合”解决方案——我已经远离杂草了! - 但我也完全重写了“最小解决方案”和随附的解释。我这样做主要是为了其他后来的读者,但您可能会从阅读新答案中获得一些价值。
【解决方案4】:
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 &lt; 4 or $^k &gt; 7)),但您似乎在 cmets 中提出了一种更优雅的书写方式。仍然(如果您不想使用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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-22
    • 2014-04-20
    • 1970-01-01
    • 2012-07-22
    相关资源
    最近更新 更多