Nilai skalar terpengaruh setelah dorongan, atau tidak ... (Raku)

12

Saya mengalami kesulitan memahami kapan dan mengapa nilai yang dipegang oleh Scalarwadah terdorong dipengaruhi setelah dorongan. Saya akan mencoba mengilustrasikan masalah yang saya hadapi dalam konteks yang lebih rumit dalam dua contoh bergaya.

* Contoh 1 * Dalam contoh pertama, skalar $ididorong ke array @bsebagai bagian dari a List. Setelah push, nilai yang dimiliki oleh skalar secara eksplisit diperbarui dalam iterasi selanjutnya dari for loop menggunakan $i++instruksi. Pembaruan ini memiliki efek pada nilai dalam array @b: pada akhir loop for, @b[0;0]sama dengan 3, dan tidak lagi 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;

Contoh output 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

* Contoh 2 * Dalam contoh kedua, skalar $iadalah variabel loop. Meskipun $idiperbarui setelah telah mendorong (sekarang secara implisit bukan eksplisit), nilai $idalam array @ctidak tidak berubah setelah push; yaitu setelah for loop, masih 2, tidak 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;;

Contoh output 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

Pertanyaan: Mengapa $idi @bdalam contoh 1 diperbarui setelah push, sementara $idi @cdalam contoh 2 tidak?

sunting : Mengikuti komentar @ timotimo, saya memasukkan output dari .WHEREdalam contoh. Ini menunjukkan skalar-identitas (WHICH / logis) $itetap sama, sementara alamat memorinya berubah melalui berbagai iterasi loop. Tetapi itu tidak menjelaskan mengapa dalam contoh 2 skalar yang didorong tetap terikat pada identitas WHICH yang sama dalam kombinasi dengan alamat lama ("448).

ozzy
sumber
2
saya dapat memberi Anda jawaban mengapa WHICH tampaknya tetap sama; lihat implementasinya: github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8 - itu hanya tergantung pada deskriptor yang digunakan, yang merupakan objek kecil yang menampung hal-hal seperti nama variabel, dan batasan tipe. jika Anda menggunakan .WHEREalih-alih, .WHICHAnda dapat melihat bahwa skalar sebenarnya adalah objek yang berbeda setiap kali di loop. Itu terjadi karena blok runcing "disebut", dan tanda tangan "terikat" pada setiap panggilan.
timotimo
@raiph Selama loop, Contoh 1 menunjukkan pola yang sama dengan Contoh 2: keduanya memiliki alamat yang berubah dilaporkan oleh .WHERE, yang mengatakan, saya setuju. Tetapi dalam dirinya sendiri itu tidak menjelaskan mengapa Contoh 2 sampai pada akhir yang berbeda dari Contoh 1.
ozzy

Jawaban:

5

Nilai skalar hanyalah sebuah wadah. Anda dapat menganggap mereka sebagai semacam penunjuk pintar, bukan nilai primitif.

Jika Anda melakukan tugas

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

Anda mengubah nilai skalar, wadah tetap sama.

Mempertimbangkan

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

dan

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;

Keduanya berfungsi seperti yang diharapkan. Tetapi: Dalam kedua kasus, benda dalam daftar tidak dapat berubah lagi, karena tidak ada wadah.

@b[4;0] = 99; 

karena itu akan mati. Jadi gunakan saja variabel loop, kan?

Tidak.

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

bahkan jika kita beralih pada daftar hal-hal yang bisa berubah.

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

Jadi tidak ada alias yang terjadi di sini, sebaliknya variabel loop selalu wadah yang sama dan mendapatkan nilai yang ditetapkan yang berasal dari wadah lain.

Kita bisa melakukan ini.

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

Cara untuk membuat "hal" bisa berubah menggunakan variabel perantara.

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

bekerja dengan baik. Atau lebih pendek dan lebih banyak dalam konteks aslinya

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)]

Lihat juga:

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

Holli
sumber
1
Alih-alih ($x,1), Anda juga bisa melakukan [$x,1]yang akan membuat wadah baru (juga untuk 1, BTW)
Elizabeth Mattijsen
@ ElizabethMattijsen Tapi kemudian Array yang melakukan "pengangkatan" ya?
Holli
Tidak yakin apa yang Anda maksud dengan "mengangkat", tetapi jika Anda menyimpan nilai-nilai pada penciptaan, maka ya.
Elizabeth Mattijsen
@Holli Terima kasih atas balasan Anda. Saya tidak yakin apakah itu menjawab pertanyaan itu. Jawaban Anda berfokus pada sifat tidak berubah dari wadah, yang saya pikir saya mengerti. Apa yang saya tidak mengerti adalah mengapa dalam contoh pertama kontainer yang didorong $ i - atau lebih baik: nilainya - diperbarui setelah push, sedangkan dalam contoh kedua nilai kontainer yang didorong tetap terikat secara statis dengan nilai saat ini. dari dorongan. Contoh pertama masuk akal bagi saya (container adalah pointer ke Intobjek -> Intdiganti untuk loop -> container menunjuk ke yang baru Int), tetapi yang kedua tidak.
Ozzy
@Holli saya akan mencoba mengklarifikasi pertanyaan.
Ozzy
3

Setelah bermain dengan dan memikirkan pertanyaan saya di atas untuk beberapa waktu, saya akan bertaruh jawaban ... Ini dugaan murni di pihak saya, jadi jangan ragu untuk mengatakan itu tidak masuk akal jika itu, dan jika Anda kebetulan tahu, Mengapa...

Pada contoh pertama, $ididefinisikan di luar lingkup leksikal for for. Akibatnya, $iada independen dari loop dan iterasinya. Ketika $idireferensikan dari dalam loop, hanya ada satu $iyang bisa terpengaruh. Inilah $iyang didorong ke dalam @b, dan isinya dimodifikasi setelah itu dalam loop.

Pada contoh kedua, $ididefinisikan di dalam lingkup leksikal for for. Seperti @timotimo tunjukkan, blok runcing dipanggil untuk setiap iterasi, seperti subrutin; $iOleh karena itu baru dinyatakan untuk setiap iterasi, dan dicakup dalam blok masing-masing. Ketika $idireferensikan di dalam loop, referensi adalah untuk blok-iterasi-spesifik $i, yang biasanya tidak ada ketika iterasi loop masing-masing berakhir. Tetapi karena pada titik tertentu $ididorong ke @c, referensi ke $inilai holding spesifik blok-iterasi 2tidak dapat dihapus oleh pengumpul sampah setelah penghentian iterasi. Itu akan tetap ada ..., tetapi masih berbeda dari $iiterasinya nanti.

ozzy
sumber
@raiph Terima kasih. Saya akan melakukannya. Mungkin seseorang dengan wawasan lebih dari saya dapat (kembali) mengucapkan jawaban dengan benar. Bagaimanapun saya tidak akan menerima jawaban saya sendiri sebagai benar sampai itu dikonfirmasi (atau ditingkatkan) oleh mereka yang tahu (daripada menebak, seperti saya sendiri).
Ozzy