Der Skalarwert wird nach dem Push beeinflusst oder nicht… (Raku)

12

Ich habe Schwierigkeiten zu verstehen, wann und warum der Wert eines ScalarPush-Containers nach dem Push beeinflusst wird. Ich werde versuchen, das Problem, auf das ich in einem komplizierteren Kontext gestoßen bin, in zwei stilisierten Beispielen zu veranschaulichen.

* Beispiel 1 * Im ersten Beispiel wird ein Skalar als Teil von a $iauf ein Array verschoben . Nach dem Push wird der vom Skalar gehaltene Wert in späteren Iterationen der for-Schleife unter Verwendung der Anweisung explizit aktualisiert . Diese Aktualisierungen wirken sich auf den Wert im Array aus : Am Ende der for-Schleife ist gleich und nicht mehr gleich .@bList$i++@b@b[0;0]32

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;

Ausgabebeispiel 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

* Beispiel 2 * Im zweiten Beispiel ist der Skalar $idie Schleifenvariable. Auch wenn $iaktualisiert wird , nachdem es (jetzt eher implizit als explizit), den Wert gedrückt wurde $iin Array @cist nicht ändern , nachdem der Druck; dh nach der for-Schleife ist es immer noch 2nicht 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;;

Ausgabebeispiel 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

Frage: Warum ist $iin @bBeispiel 1 nach dem Push aktualisiert, während $iin @cBeispiel 2 ist das nicht?

edit : Nach dem Kommentar von @ timotimo habe ich die Ausgabe von .WHEREin die Beispiele aufgenommen. Dies zeigt, dass die (WELCHE / logische) Skalaridentität von gleich $ibleibt, während sich ihre Speicheradresse durch die verschiedenen Schleifeniterationen ändert. Es erklärt jedoch nicht, warum in Beispiel 2 der Push-Skalar in Kombination mit einer alten Adresse ("448) an dieselbe WHICH-Identität gebunden bleibt.

ozzy
quelle
2
Ich kann Ihnen die Antwort geben, warum das, was gleich zu bleiben scheint; Schauen Sie sich die Implementierung an: github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8 - es hängt nur vom verwendeten Deskriptor ab, bei dem es sich um ein kleines Objekt handelt, das Dinge wie den Namen von enthält die Variable und die Typbeschränkung. Wenn Sie .WHEREstattdessen verwenden .WHICH, können Sie sehen, dass der Skalar jedes Mal um die Schleife herum ein anderes Objekt ist. Dies geschieht, weil spitze Blöcke "aufgerufen" werden und die Signatur bei jedem Aufruf "gebunden" wird.
Timotimo
@raiph Während der Schleife zeigt Beispiel 1 das gleiche Muster wie Beispiel 2: Beide haben sich ändernde Adressen, die von .WHERE gemeldet werden, was bezeichnend ist, da stimme ich zu. Aber an sich erklärt es nicht, warum Beispiel 2 zu einem anderen Ende kommt als Beispiel 1.
ozzy

Antworten:

5

Ein Skalarwert ist nur ein Container. Sie können sie sich eher als eine Art intelligenten Zeiger als als einen primitiven Wert vorstellen.

Wenn Sie eine Aufgabe machen

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

Wenn Sie den Skalarwert ändern, bleibt der Container gleich.

Erwägen

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

und

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;

Beide funktionieren wie erwartet. Aber: In beiden Fällen ist das Objekt in der Liste nicht mehr veränderbar, da es keinen Container gibt.

@b[4;0] = 99; 

wird daher sterben. Verwenden Sie also einfach die Schleifenvariable, oder?

Nein.

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

selbst wenn wir eine Liste veränderlicher Dinge durchlaufen.

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

Hier findet also kein Aliasing statt, stattdessen ist die Schleifenvariable immer derselbe Container und erhält zugewiesene Werte, die von den anderen Containern stammen.

Wir können das aber tun.

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

Eine Möglichkeit, "das Ding" veränderbar zu machen, ist die Verwendung einer Zwischenvariablen.

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

funktioniert gut. Oder kürzer und mehr im ursprünglichen Kontext

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

Siehe auch:

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

Holli
quelle
1
Statt ($x,1), können Sie auch tun , [$x,1]das würde einen neuen Container (auch für erstellen 1, BTW)
Elizabeth Mattijsen
@ ElizabethMattijsen Aber dann ist es das Array, das das "Heben" ja macht?
Holli
Ich bin mir nicht sicher, was Sie unter "Heben" verstehen, aber wenn Sie die Werte bei der Erstellung containerisieren, dann ja.
Elizabeth Mattijsen
@ Holli Danke für deine Antwort. Ich bin mir nicht sicher, ob es die Frage anspricht. Ihre Antwort konzentriert sich auf die Veränderlichkeit des Containers, die ich zu verstehen glaube. Was ich nicht verstehe, ist, warum im ersten Beispiel der Push-Container $ i - oder besser: sein Wert - nach dem Push aktualisiert wird, während im zweiten Beispiel der Wert des Push-Containers im Moment statisch an den Wert gebunden bleibt des Stoßes. Das erste Beispiel macht für mich Sinn (Container ist Zeiger auf IntObjekt -> Intwird in for-Schleife ersetzt -> Container zeigt auf neu Int), das zweite nicht.
Ozzy
@ Holli Ich werde versuchen, die Frage zu klären.
Ozzy
3

Nachdem ich einige Zeit mit meiner obigen Frage gespielt und darüber nachgedacht habe, werde ich eine Antwort wetten ... Es ist eine reine Vermutung meinerseits, also zögern Sie nicht zu sagen, dass es unsinnig ist, wenn es so ist und wenn Sie es zufällig wissen, Warum...

Im ersten Beispiel $iwird außerhalb des lexikalischen Bereichs der for-Schleife definiert. Folglich $iexistiert unabhängig von der Schleife und ihren Iterationen. Wenn $iinnerhalb der Schleife auf sie verwiesen wird $i, kann nur eine betroffen sein. Es ist dies $i, das hineingeschoben @bwird und dessen Inhalt anschließend in der Schleife geändert wird.

Im zweiten Beispiel $iwird innerhalb des lexikalischen Bereichs der for-Schleife definiert. Wie @timotimo hervorhob, wird der spitze Block für jede Iteration aufgerufen, wie eine Unterroutine; $iwird daher für jede Iteration neu deklariert und auf den jeweiligen Block beschränkt. Wenn $iinnerhalb der Schleife referenziert wird, bezieht sich die Referenz auf die blockiterationsspezifische $i, die normalerweise nicht mehr existiert, wenn die jeweilige Schleifeniteration endet. Weil aber an einem gewissen Punkt $izu gedrückt wird @c, die Bezugnahme auf den Block-Iteration spezifischen $iHaltewert 2kann nicht durch den Speicherbereinigungs nach Beendigung der Iteration gelöscht werden. Es wird bestehen bleiben ..., sich aber immer noch von $ispäteren Iterationen unterscheiden.

ozzy
quelle
@raiph Danke. Ich werde das tun. Vielleicht kann jemand mit mehr Einsicht als ich die Antwort richtig (neu) formulieren. Ich werde meine eigene Antwort sowieso nicht als richtig akzeptieren, bis sie von denen bestätigt (oder verbessert) wird, die es wissen (anstatt zu raten, wie ich selbst).
Ozzy