Скалярне значення впливає після натискання, чи ні… (Раку)


12

У мене є труднощі зрозуміти, коли і чому Scalarпісля натискання впливає значення, яке утримує контейнер, що висувається . Я спробую проілюструвати проблему, яку я зіткнувся у більш складному контексті на двох стилізованих прикладах.

* Приклад 1 * У першому прикладі скаляр $iвисувається на масив @bяк частина a 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 не є?

редагувати : Після коментаря @ timotimo я включив висновок .WHEREу приклади. Це показує (ЯКА / логічна) скалярна ідентичність $iзалишається однаковою, в той час як її пам'ять змінюється через різні ітерації циклу. Але це не пояснює, чому в прикладі 2 висунутий скаляр залишається прив’язаним до тієї ж, ЯКА Ідентичність у поєднанні зі старою адресою ("448).


2
я можу дати вам відповідь на те, чому, здається, залишається таким же; подивіться на реалізацію: github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8 - це залежить лише від використовуваного дескриптора, який є маленьким об'єктом, який містить такі речі, як ім'я змінна та обмеження типу. якщо ви використовуєте .WHEREзамість .WHICHвас, ви можете бачити, що скаляр насправді є різним об'єктом кожного разу навколо циклу. Це відбувається тому, що точкові блоки "викликаються", а підпис "прив'язується" до кожного дзвінка.
тимотімо

@raiph Під час циклу, Приклад 1 показує таку ж схему, що і Приклад 2: обидва мають зміни адрес, про які повідомляє .WHERE, що говорить, я згоден. Але саме по собі воно не пояснює, чому Приклад 2 закінчується іншим, ніж Приклад 1.
ozzy

Відповіді:


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, до речі)
Елізабет Mattijsen

@ElizabethMattijsen Але тоді саме масив робить "підйом" так?
Холлі

Не впевнений, що ви маєте на увазі підняттям, але якщо ви зберігаєте значення при створенні, то так.
Елізабет Маттійсен

@Holli Дякую за вашу відповідь Я не впевнений, чи стосується це питання. Ваша відповідь фокусується на змінності контейнера, яку я думаю, що я розумію. Я не розумію, чому в першому прикладі висунутий контейнер $ i - або краще: його значення - оновлюється після натискання, тоді як у другому прикладі значення висунутого контейнера залишається статично прив'язаним до значення на даний момент поштовху. Перший приклад має певний сенс для мене (контейнер - вказівник на Intоб’єкт -> Intзамінюється на цикл -> контейнер вказує на новий 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.