标量值在推送后是否受到影响……(Raku)


12

我很难理解在推送Scalar之后何时以及为什么推送的容器所持有的价值会受到影响。我将在两个风格化的示例中尝试更复杂地说明我遇到的问题。

*示例1 *在第一个示例中,标量作为a的一部分$i被推到数组@bList。推送之后,使用$i++指令在for循环的后续迭代中显式更新标量所拥有的值。这些更新会影响数组中的值@b:在for循环的末尾,@b[0;0]等于3,不再等于2

my @b;
my $i=0;
for 1..3 -> $x {
  $i++;
  say 'Loose var $i: ', $i.VAR.WHICH, " ", $i.VAR.WHERE;
  if $x == 2 {
     @b.push(($i,1));
     say 'Pushed $i   : ', @b[0;0].VAR.WHICH, " ", @b[0;0].VAR.WHERE;
  }
}
say "Post for-loop";
say "Array       : ", @b;
say 'Pushed $i   : ', @b[0;0].VAR.WHICH, " ", @b[0;0].VAR.WHERE;

输出示例1:

Loose var $i: Scalar|94884317665520 139900170768608
Loose var $i: Scalar|94884317665520 139900170768648
Pushed $i   : Scalar|94884317665520 139900170768648
Loose var $i: Scalar|94884317665520 139900170768688
Post for-loop
Array       : [(3 1)]
Pushed $i   : Scalar|94884317665520 139900170768688

*示例2 *在第二个示例中,标量$i是循环变量。尽管$i它已被推后(现在的隐含而不是明确地)的值更新$i数组@c没有 推后更改; 即在for循环之后,它仍然2不是3

my @c;
for 1..3 -> $i {
  say 'Loose var $i: ', $i.VAR.WHICH, " ", $i.VAR.WHERE;
  if $i == 2 {
     @c.push(($i,1));
     say 'Pushed $i   : ', @c[0;0].VAR.WHICH, " ", @c[0;0].VAR.WHERE;
  }
}
say "Post for-loop";
say "Array       : ", @c;
say 'Pushed $i   : ', @c[0;0].VAR.WHICH, " ", @c[0;0].VAR.WHERE;;

输出示例2:

Loose var $i: Scalar|94289037186864 139683885277408
Loose var $i: Scalar|94289037186864 139683885277448
Pushed $i   : Scalar|94289037186864 139683885277448
Loose var $i: Scalar|94289037186864 139683885277488
Post for-loop
Array       : [(2 1)]
Pushed $i   : Scalar|94289037186864 139683885277448

问:为什么$i@b例1推后更新,而$i@c例2中是不是?

编辑:在@timotimo的评论之后,我.WHERE在示例中包括的输出。这显示了保持的(WHICH /逻辑)标量身份$i不变,而其内存地址通过各种循环迭代而改变。但是,这并没有解释为什么在示例2中,推入的标量与旧地址(“ 448”)结合仍然与同一WHICH身份绑定。


2
我可以为您解答为什么WHICH似乎保持不变;看一下实现:github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8-它仅取决于所使用的描述符,这是一个小对象,其中包含诸如名称之类的东西变量和类型约束。如果使用.WHERE而不是,.WHICH则每次循环时,标量实际上都是一个不同的对象。之所以会这样,是因为尖锐的块被“调用”,并且签名在每次调用时都被“绑定”。
timotimo '19

@raiph在循环中,示例1显示与示例2相同的模式:两者都有.WHERE报告的更改地址,这表明,我同意。但是它本身并不能解释为什么示例2与示例1的结局不同
。– ozzy

Answers:


5

标量值只是一个容器。您可以将它们视为一种智能指针,而不是原始值。

如果您做作业

$foo = "something"; #or
$bar++;

您在更改标量值时,容器保持不变。

考虑

my @b; 
my $i=0; 
for 1..5 -> $x { 
  $i++; 
  @b.push(($i<>,1)); # decontainerize $i and use the bare value
} 
say @b;

my @b; 
my $i=0; 
for 1..5 -> $x { 
  $i := $i + 1;  # replaces the container with value / change value
  @b.push(($i,1)); 
} 
say @b;

两者都按预期工作。但是:在这两种情况下,列表中的东西都不再可变了,因为没有容器。

@b[4;0] = 99; 

因此会死。那么就使用循环变量吧?

没有。

for 1..5 -> $x { 
  @b.push(($x,1)); # 
} 
@b[4;0] = 99; #dies

即使我们迭代了一系列可变的东西。

my $one = 1;
my $two = 2;
my $three = 3;
my $four = 4;
my $five = 5;

for ($one, $two, $three, $four, $five) -> $x { 
  @b.push(($x,1)); 
} 
@b[4;0] = 99; #dies

因此,这里没有发生别名,而是循环变量始终是同一容器,并获取来自其他容器的赋值。

我们可以做到这一点。

for ($one, $two, $three, $four, $five) <-> $x { 
  @b.push(($x,1)); 
} 
@b[4;0] = 99; # works

for ($one, $two, $three, $four, $five) -> $x is rw { 
  @b.push(($x,1)); 
} 
@b[4;0] = 99; # works too

使“事物”可变的一种方法是使用中间变量。

for 1..5 -> $x { 
  my $j = $x;
  @b.push(($j,1)); # a new container 
} 
@b[4;0] = 99;

工作良好。或更短或更原始的背景

my @b; 
my $i=0; 
for 1..5 -> $x { 
  $i++; 
  @b.push((my $ = $i, 1)); # a new anonymous container
} 
@b[4;0] = 99;
say @b; # [(1 1) (2 1) (3 1) (4 1) (99 1)]

也可以看看:

https://perl6advent.wordpress.com/2017/12/02/#theoneandonly https://docs.perl6.org/language/containers


1
相反的($x,1),你也可以做到[$x,1]创建一个新的容器(也可用于1,BTW)
伊丽莎白Mattijsen

@ElizabethMattijsen但是,“解除”是数组吗?
霍利(Holli)

不确定“提升”的含义,但是如果将创建时的值容器化,则可以。
伊丽莎白·马蒂森

@Holli感谢您的答复。我不确定它是否解决了这个问题。您的答案集中在容器的可变性上,我认为我理解这一点。我不明白的是,为什么在第一个示例中,被推入的容器$ i-或更佳:其值-在推入之后进行更新,而在第二个示例中,被推入的容器的值目前静态地与该值绑定的推动。第一个示例对我来说有意义(容器是指向Int对象的指针-> Int被替换为for循环->容器指向new Int),但第二个示例则没有。
ozzy

@Holli我将尝试澄清这个问题。
ozzy

3

经过一段时间的思考和思考我的上述问题之后,我会下一个答案...这纯粹是我的猜测,因此请随意说这是无稽之谈,如果您碰巧知道,为什么...

在第一个示例中,$i在for循环的词法范围之外定义。因此,$i存在与循环及其迭代无关。当$i从循环内部进行引用时,只有一个$i会受到影响。正是这个$i被推入@b,并随后在循环中修改其内容。

在第二个示例中,$i在for循环的词法范围内定义。正如@timotimo指出的那样,每次迭代都会调用指定的块get,就像子例程一样;$i因此,每次迭代都重新声明它,并将其范围限定在相应的块中。当$i在循环内部引用时,该引用是针对特定块迭代的$i,当各个循环迭代结束时,该引用通常将不复存在。但是由于在某个时刻$i被推送到@c,因此在迭代终止后,垃圾回收器无法删除对特定于块迭代的$i保持值的引用2。它将继续存在...但是与$i以后的迭代中仍然有所不同。


@raiph谢谢。我会去做。也许一个比我自己更有洞察力的人可以正确地(重新)改写答案。在知道的人(而不是像我一样猜测)确认(或改进)答案之前,我将不会接受我自己的答案是正确的。
ozzy,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.