Valore scalare influenzato dopo il push, o no ... (Raku)


12

Ho difficoltà a capire quando e perché il valore detenuto da un Scalarcontenitore push viene influenzato dopo la spinta. Proverò a illustrare il problema che ho incontrato in un contesto più complicato in due esempi stilizzati.

* Esempio 1 * Nel primo esempio, uno scalare $iviene inserito in un array @bcome parte di a List. Dopo il push, il valore mantenuto dallo scalare viene esplicitamente aggiornato nelle successive iterazioni del ciclo for usando l' $i++istruzione. Questi aggiornamenti hanno un effetto sul valore dell'array @b: alla fine del ciclo for, @b[0;0]è uguale 3e non più a 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;

Esempio di 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

* Esempio 2 * Nel secondo esempio, lo scalare $iè la variabile loop. Anche se $iviene aggiornato dopo che è stato spinto (ora implicitamente piuttosto che esplicitamente), il valore di $ia matrice @cnon non cambia dopo la spinta; cioè dopo il ciclo for, è ancora 2, no 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;;

Esempio di 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

Domanda: Perché è $iin @bnell'esempio 1 aggiornato dopo la spinta, mentre $iin @cnell'esempio 2 non è?

modifica : seguendo il commento di @ timotimo, ho incluso l'output di .WHEREnegli esempi. Ciò mostra che l'identità scalare (WHICH / logica) di $irimane invariata, mentre il suo indirizzo di memoria cambia attraverso le varie iterazioni del ciclo. Ma non spiega perché nell'esempio 2 lo scalare spinto rimanga legato alla stessa identità WHICH in combinazione con un vecchio indirizzo ("448).


2
posso darti la risposta al perché il WHICH sembra rimanere lo stesso; guarda l'implementazione: github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8 - dipende solo dal descrittore in uso, che è un piccolo oggetto che contiene elementi come il nome di la variabile e il vincolo di tipo. se usi .WHEREinvece di .WHICHte puoi vedere che lo scalare è in realtà un oggetto diverso ogni volta intorno al ciclo. Ciò accade perché i blocchi appuntiti vengono "chiamati" e la firma viene "associata" ad ogni chiamata.
timotimo,

@raiph Durante il ciclo, l'Esempio 1 mostra lo stesso modello dell'Esempio 2: entrambi hanno indirizzi cambianti riportati da .WHERE, che sta dicendo, sono d'accordo. Ma di per sé non spiega perché l'Esempio 2 abbia una fine diversa
dall'Esempio

Risposte:


5

Un valore scalare è solo un contenitore. Puoi considerarli come una sorta di puntatore intelligente, piuttosto che un valore primitivo.

Se fai un incarico

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

stai cambiando il valore scalare, il contenitore rimane lo stesso.

Prendere in considerazione

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

e

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;

Entrambi funzionano come previsto. Ma: in entrambi i casi, la cosa nell'elenco non è più modificabile, perché non esiste un contenitore.

@b[4;0] = 99; 

morirà quindi. Quindi usa la variabile loop quindi, giusto?

No.

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

anche se ripetiamo un elenco di cose mutevoli.

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

Quindi non si verifica alcun alias qui, invece la variabile loop è sempre lo stesso contenitore e ottiene i valori assegnati che provengono dagli altri contenitori.

Possiamo farlo però.

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

Un modo per rendere mutevole "la cosa" è usare una variabile intermedia.

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

funziona bene. O più breve e più nel contesto originale

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

Guarda anche:

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


1
Invece di ($x,1), potresti anche fare [$x,1]ciò che creerebbe un nuovo contenitore (anche per 1BTW)
Elizabeth Mattijsen,

@ElizabethMattijsen Ma poi è l'array che fa il "sollevamento" sì?
Holli,

Non sei sicuro di cosa intendi per "sollevamento", ma se containerizzi i valori al momento della creazione, allora sì.
Elizabeth Mattijsen,

@Holli Grazie per la tua risposta. Non sono sicuro se affronti la domanda però. La tua risposta si concentra sulla mutabilità del contenitore, che penso di aver capito. Quello che non capisco è perché nel primo esempio il contenitore push $ i - o meglio: il suo valore - viene aggiornato dopo il push, mentre nel secondo esempio il valore del contenitore push rimane legato staticamente al valore al momento della spinta. Il primo esempio ha un senso per me (il contenitore è puntatore Intall'oggetto -> Intviene sostituito per loop -> il contenitore punta a nuovo Int), ma il secondo no.
ozzy

@Holli proverò a chiarire la domanda.
ozzy

3

Dopo aver giocato e pensato alla mia domanda sopra per qualche tempo, scommetterò una risposta ... È pura congettura da parte mia, quindi sentiti libero di dire che non ha senso se lo è, e se ti capita di sapere, perché...

Nel primo esempio, $iè definito al di fuori dell'ambito lessicale del ciclo for. Di conseguenza, $iesiste indipendentemente dal ciclo e dalle sue iterazioni. Quando $iviene fatto riferimento dall'interno del loop, ce n'è solo uno $iche può essere influenzato. È questo $iche viene inserito @b, e il suo contenuto viene modificato successivamente nel loop.

Nel secondo esempio, $iè definito all'interno dell'ambito lessicale del ciclo for. Come sottolineato da @timotimo, il blocco appuntito viene chiamato per ogni iterazione, come una subroutine; $iviene quindi dichiarato di recente per ogni iterazione e sottoposto a ambito per il rispettivo blocco. Quando $iviene fatto riferimento all'interno del ciclo, il riferimento è al blocco specifico dell'iterazione $i, che normalmente cesserebbe di esistere al termine della rispettiva iterazione del ciclo. Ma poiché a un certo punto $iviene spinto a @c, il garbage collector non può cancellare il riferimento al $ivalore di mantenimento specifico dell'iterazione di blocchi 2dopo la fine dell'iterazione. Rimarrà in esistenza ..., ma sarà comunque diverso dalle $isuccessive iterazioni.


@raiph Grazie. Lo farò. Forse quel qualcuno con più intuizioni di me può (ri) pronunciare la risposta correttamente. Non accetterò comunque la mia risposta come corretta fino a quando non sarà confermata (o migliorata) da coloro che conoscono (piuttosto che indovinare, come me).
ozzy,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.