Скалярное значение зависит от толчка или нет ... (Раку) - PullRequest
11 голосов
/ 05 октября 2019

Мне сложно понять, когда и почему значение, сохраняемое помещенным Scalar контейнером, изменяется после нажатия. Я попытаюсь проиллюстрировать проблему, с которой я столкнулся, в более сложном контексте на двух стилизованных примерах.

* Пример 1 * В первом примере скаляр $i помещается в массив @b как часть List. После отправки значение, хранящееся в скаляре, явно обновляется в последующих итерациях цикла for с помощью инструкции $i++. Эти обновления влияют на значение в массиве @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 нет?

edit : После комментария @ timotimo я включилвывод .WHERE в примерах. Это показывает, что (WHICH / логический) скалярный идентификатор $i остается тем же самым, в то время как его адрес памяти изменяется через различные итерации цикла. Но это не объясняет, почему в примере 2 выдвинутый скаляр остается привязанным к тому же WHICH-идентификатору в сочетании со старым адресом ("448).

Ответы [ 2 ]

5 голосов
/ 05 октября 2019

Скалярное значение - это просто контейнер. Вы можете думать о них как об интеллектуальном указателе, а не как о примитивном значении.

Если вы выполняете присваивание

$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

3 голосов
/ 06 октября 2019

Поработав и подумав над моим вышеупомянутым вопросом в течение некоторого времени, я поставлю на вопрос ... Это чистая догадка с моей стороны, поэтому, пожалуйста, не стесняйтесь говорить, что это бессмысленно, если это так, и если вы случитесьчтобы знать, почему ...

В первом примере $i определяется вне лексической области действия цикла for. Следовательно, $i существует независимо от цикла и его итераций. Когда на $i ссылаются из цикла, существует только один $i, который может быть затронут. Именно этот $i помещается в @b, и его содержимое впоследствии изменяется в цикле.

Во втором примере $i определяется внутри лексической области действия цикла for. Как указывал @timotimo, указанный блок get вызывается для каждой итерации, как подпрограмма;$i поэтому вновь объявляется для каждой итерации и попадает в соответствующий блок. Когда на $i ссылаются внутри цикла, ссылка на специфичную для итерации блока $i, которая обычно перестает существовать, когда заканчивается соответствующая итерация цикла. Но поскольку в какой-то момент $i передается на @c, ссылка на специфическое для итерации блока $i значение 2 не может быть удалено сборщиком мусора после завершения итерации. Он останется в существовании ..., но все равно будет отличаться от $i в последующих итерациях.

...