El valor escalar se ve afectado después del empuje, o no ... (Raku)

12

Tengo dificultades para comprender cuándo y por qué el valor que tiene un Scalarcontenedor enviado se ve afectado después del envío. Trataré de ilustrar el problema que encontré en un contexto más complicado en dos ejemplos estilizados.

* Ejemplo 1 * En el primer ejemplo, un escalar $ise inserta en una matriz @bcomo parte de a List. Después de la inserción, el valor mantenido por el escalar se actualiza explícitamente en iteraciones posteriores del bucle for utilizando la $i++instrucción. Estas actualizaciones tienen un efecto en el valor de la matriz @b: al final del ciclo for, @b[0;0]es igual a 3, y ya no lo es 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;

Ejemplo de salida 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

* Ejemplo 2 * En el segundo ejemplo, el escalar $ies la variable de bucle. A pesar de que $ise actualiza después de que ha sido empujado (ahora implícita más que explícitamente), el valor de $ien la matriz @cno no cambia después de pulsar; es decir, después del ciclo for, todavía está 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;;

Ejemplo de salida 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

Pregunta: ¿Por qué es $ien @ben el ejemplo 1 actualizado después de que el empuje, mientras que $ien @cen el ejemplo 2 no es?

editar : Siguiendo el comentario de @ timotimo, incluí la salida de .WHEREen los ejemplos. Esto muestra la identidad escalar (QUE / lógica) de $ipermanece igual, mientras que su dirección de memoria cambia a través de las diversas iteraciones de bucle. Pero no explica por qué en el ejemplo 2 el escalar empujado permanece atado a la misma identidad QUE en combinación con una dirección anterior ("448).

ozzy
fuente
2
puedo darte la respuesta de por qué lo que parece permanecer igual; mire la implementación: github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8 : solo depende del descriptor que se esté utilizando, que es un pequeño objeto que contiene cosas como el nombre de la variable y la restricción de tipo. Si usa en .WHERElugar de .WHICH, puede ver que el escalar es en realidad un objeto diferente cada vez alrededor del bucle. Eso sucede porque los bloques puntiagudos se "llaman" y la firma se "vincula" en cada llamada.
timotimo
@raiph Durante el ciclo, el Ejemplo 1 muestra el mismo patrón que el Ejemplo 2: ambos tienen direcciones cambiantes informadas por .WHERE, lo cual es revelador, estoy de acuerdo. Pero en sí mismo no explica por qué el Ejemplo 2 llega a un final diferente al Ejemplo 1.
ozzy

Respuestas:

5

Un valor escalar es solo un contenedor. Puede pensar en ellos como una especie de puntero inteligente, en lugar de un valor primitivo.

Si haces una tarea

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

Si está cambiando el valor de los escalares, el contenedor permanece igual.

Considerar

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

y

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;

Ambos funcionan como se esperaba. Pero: en ambos casos, la cosa en la lista ya no es mutable, porque no hay contenedor.

@b[4;0] = 99; 

Por lo tanto, morirá. Entonces, solo use la variable de bucle, ¿verdad?

No.

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

incluso si iteramos sobre una lista de cosas mutables.

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

Por lo tanto, no hay alias aquí, en su lugar, la variable de bucle siempre es el mismo contenedor y obtiene valores asignados que provienen de los otros contenedores.

Sin embargo, podemos hacer esto.

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

Una manera de hacer que "la cosa" sea mutable es usar una variable intermedia.

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

funciona bien. O más corto y más en el contexto original

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

Ver también:

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

Holli
fuente
1
En lugar de ($x,1), también podría hacer [$x,1]lo que crearía un nuevo contenedor (también para 1, por cierto)
Elizabeth Mattijsen
@ElizabethMattijsen Pero entonces es la matriz la que hace el "levantamiento" ¿sí?
Holli
No estoy seguro de lo que quiere decir con "levantar", pero si contiene los valores en la creación de contenedores, entonces sí.
Elizabeth Mattijsen el
@Holli Gracias por tu respuesta. Sin embargo, no estoy seguro de si aborda la pregunta. Su respuesta se centra en la mutabilidad del contenedor, que creo que entiendo. Lo que no entiendo es por qué en el primer ejemplo el contenedor insertado $ i - o mejor: su valor - se actualiza después del envío, mientras que en el segundo ejemplo el valor del contenedor enviado permanece estáticamente vinculado al valor en este momento del empuje El primer ejemplo tiene algún sentido para mí (el contenedor es puntero al Intobjeto -> Intse reemplaza por bucle -> el contenedor apunta a nuevo Int), pero el segundo no.
ozzy
@Holli Trataré de aclarar la pregunta.
ozzy
3

Después de jugar y pensar en mi pregunta anterior por un tiempo, apostaré una respuesta ... Es pura conjetura de mi parte, así que siéntase libre de decir que no tiene sentido si es así, y si usted sabe, por qué...

En el primer ejemplo, $ise define fuera del alcance léxico del bucle for. En consecuencia, $iexiste independientemente del bucle y sus iteraciones. Cuando $ise hace referencia desde el interior del bucle, solo hay uno $ique puede verse afectado. Es esto en lo $ique se empuja @b, y luego se modifica su contenido en el ciclo.

En el segundo ejemplo, $ise define dentro del alcance léxico del bucle for. Como señaló @timotimo, se llama al bloque puntiagudo para cada iteración, como una subrutina; $ipor lo tanto, se declara recientemente para cada iteración y se limita al bloque respectivo. Cuando $ise hace referencia dentro del bucle, la referencia es a la iteración de bloque específica $i, que normalmente dejaría de existir cuando finaliza la iteración del bucle respectivo. Pero debido a que en algún momento $ise empuja @c, el recolector de basura no puede eliminar la referencia al $ivalor de retención específico de la iteración de bloque 2después de la terminación de la iteración. Permanecerá en existencia ..., pero seguirá siendo diferente de las $iiteraciones posteriores.

ozzy
fuente
@raiph Gracias. Yo haré eso. Tal vez alguien con más perspicacia que yo pueda (re) formular la respuesta correctamente. De todos modos, no aceptaré mi propia respuesta como correcta hasta que sea confirmada (o mejorada) por aquellos que saben (en lugar de adivinar, como yo).
ozzy