关于Raku中数组生成序列的问题
Question on the array generating sequence in Raku
我在 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 中的代码在表面上有所不同。
这是序列运算符的一个有点有趣的应用,因为它每次产生的值都是数组。所以:
[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, 1
和 1, 0
。使用运算符压缩成对应用它,这意味着它将计算 0 + 1, 1 + 0
。整体结果是数组 [1,1]
.
对块的第二次调用得到 [1,1]
。它形成列表 0, 1, 1
和 1, 1, 0
,然后 zip 运算符再次形成成对加法,即 0 + 1, 1 + 1, 1 + 0
。整体结果是数组 [1,2,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。
这就是 |
滑移运算符的用武之地,它将给定的数组滑移到它所在的列表上下文中。让我们回到 @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]
以此类推进入无限!
我希望这有助于解释发生了什么?
我在 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 中的代码在表面上有所不同。
这是序列运算符的一个有点有趣的应用,因为它每次产生的值都是数组。所以:
[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, 1
和 1, 0
。使用运算符压缩成对应用它,这意味着它将计算 0 + 1, 1 + 0
。整体结果是数组 [1,1]
.
对块的第二次调用得到 [1,1]
。它形成列表 0, 1, 1
和 1, 1, 0
,然后 zip 运算符再次形成成对加法,即 0 + 1, 1 + 1, 1 + 0
。整体结果是数组 [1,2,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。
这就是 |
滑移运算符的用武之地,它将给定的数组滑移到它所在的列表上下文中。让我们回到 @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]
以此类推进入无限!
我希望这有助于解释发生了什么?