【问题标题】:Question on the array generating sequence in Raku关于 Raku 中的数组生成顺序的问题
【发布时间】:2019-11-21 14:07:56
【问题描述】:

我在rosettacode 遇到过这段代码

my @pascal = [1], { [0, |$_ Z+ |$_, 0] } ... Inf;
.say for @pascal[^4];
# ==>
# [1]
# [1 1]
# [1 2 1]
# [1 3 3 1]

在显式生成器块中,我知道单个运算符(如列表展平| 和 zip 运算符Z+)是如何工作的,但我很难理解它们如何协作生成下一个数组。有人可以详细解释它是如何工作的吗?谢谢。

注意:为简洁起见,代码略有重新排列,即它与 Rosetta 中的代码在表面上有所不同。

【问题讨论】:

    标签: sequence raku pascals-triangle


    【解决方案1】:

    那么让我们来看看发生了什么。

    首先是序列生成器...,它需要一个起始值列表、一个代码块本身和一个结束点。

    它使用起始值来生成列表中的每个下一项,所以让我们从 @pascal[0] 开始,这很简单:[1]

    对于@pascal[1],我们使用@pascal[0] 中的值调用代码块,如下所示:

    sub ( $array ) { 
      [0, |$array Z+ |$array, 0] 
    }
    

    你会注意到我已经把它变成了一个潜艇,这只是为了让我可以更容易地解释事情。在匿名代码块中,$_ 是传入的数据,我称之为$array。那么$array == [1]时我们运行什么代码呢?

    [0, |[1] Z+ |[1], 0] => [1,1] 
    

    所以这里有一个问题,如果我们不使用 | 会发生什么?

    [0, [1] Z+ [1], 0] => [1,1] 
    

    都是一样的!那为什么会在那里?那么如果我们设置$array == [3] 会发生什么?

    [0, [3] Z+ [3], 0] => [1,1] 
    

    奇怪吗?不,因为Z+ 会这样转换:

    [0, [3] Z+ [3], 0] => [0 + [3], [3] + 0] => [1,1] 
    

    Z 通过使用给定运算符+ 压缩元素来创建一个新列表。 + 正在进行数值计算,数组的数值表示是其中元素的数量,在这种情况下,两次都是 1。

    这就是| slip 运算符的用武之地,它将给定的数组滑动到它所在的列表上下文中打开合并。所以让我们回到@pascal[1]

    [0, |[1] Z+ |[1], 0] => [0, 1 Z+ 1, 0] => [0 + 1, 1 + 0] => [1,1] 
    

    好的.. 所以@pascal[2] 正在调用同一个块,但现在传入[1,1]

    [0, |[1, 1] Z+ |[1, 1], 0] => [0, 1, 1 Z+ 1, 1, 0] => [0 + 1, 1 + 1, 1 + 0] => [1,2,1] 
    

    等到无限!

    我希望这有助于解释发生了什么?

    【讨论】:

    • 非常感谢您的全面回答。正是[ ] 中的逗号具有误导性。我把它们当作一种逗号,它显示了序列的进程,而它们只不过是简单的旧逗号,它将数组元素彼此分开。当然,在查看sub 的连续调用后,我意识到了这一点:)
    【解决方案2】:

    这是序列运算符的一个有点有趣的应用,因为它每次产生的值都是数组。所以:

    • [1] 是序列开头生成的第一个数组
    • 该块指示如何生成序列中的下一个数组。它需要一个参数,即 previous 序列值
    • Inf 永远不会匹配,因此序列将永远继续

    一个更简单的例子可能会有所帮助:序列[1], [1,1], [1,1,1], ...。也就是说,我们想要生成一个数组,它是序列中的前一项,末尾有一个额外的1。我们可以这样做:

    my @ones = [1], { [|$_, 1] } ... Inf;
    .say for @ones[^4];
    

    制作:

    [1]
    [1 1]
    [1 1 1]
    [1 1 1 1]
    

    第一次调用该块时,它会在$_ 中获得[1]。我们用| 滑动它,从而产生一个数组[1, 1]。第二次将其传递给块,依此类推。

    所以,分解[0, |$_ Z+ |$_, 0]。外面的方括号是一个数组作曲家。其余的可以读作0, |$_|$_, 0 压缩,使用+ 操作符压缩。

    第一个阻塞调用将传递[1],因此我们将压缩0, 11, 0。使用运算符进行压缩将成对应用,这意味着它将计算0 + 1, 1 + 0。整体结果是数组[1,1]

    对块的第二次调用得到[1,1]。它形成列表0, 1, 11, 1, 0,然后zip 运算符再次形成成对加法,即0 + 1, 1 + 1, 1 + 0。整体结果是数组[1,2,1]

    实际上,结果每次都会增长一个元素,这是由前一个结果的成对相加得到的,两端都用零填充。

    【讨论】:

    • 感谢您详细而富有启发性的回答。它逐步展示了[ ] s 如何用于在代码生成器块内生成新数组。总而言之,它与@Scimon 的答案一样有用和全面。它帮助我巩固了代码块中的这些运算符如何工作以生成 Pascal 序列。
    猜你喜欢
    • 2020-03-09
    • 1970-01-01
    • 2018-12-09
    • 1970-01-01
    • 2022-01-26
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多