プッシュ後にスカラー値が影響を受けるかどうか…(楽)


12

プッシュされたScalarコンテナーによって保持されている値がプッシュ後に影響を受ける時期と理由を理解するのが困難です。2つの様式化された例で、より複雑な状況で遭遇した問題を説明しようと思います。

*例1 *最初の例では、スカラー$iがの@b一部として配列にプッシュされListます。プッシュ後、スカラーが保持する値は、$i++命令を使用したforループの後の反復で明示的に更新されます。これらの更新は、配列の値に影響を及ぼします。for @bループの最後で、@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 * 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例に含めました。これは、(WHICH /論理)スカラーIDが$i変化しない一方で、メモリアドレスがさまざまなループの反復によって変化することを示しています。しかし、例2でプッシュされたスカラーが古いアドレス( "448")と組み合わせて同じWHICH-identityに関連付けられたままになる理由は説明されていません。


2
WHICHが変わらないように思われる理由をお答えします。実装を見てください:github.com/rakudo/rakudo/blob/master/src/core.c/Scalar.pm6#L8-使用されている記述子にのみ依存します。これは、名前のようなものを保持する小さなオブジェクトです変数、および型制約。.WHERE代わりにを使用する.WHICHと、スカラーがループのたびに実際には異なるオブジェクトであることがわかります。これは、先のとがったブロックが「呼び出され」、署名が各呼び出しで「バインド」されるために発生します。
timotimo

@raiphループ中、例1は例2と同じパターンを示します。どちらも.WHEREによって報告されるアドレスの変更があります。例2例1とは異なる結末になるしかし、なぜ、それ自体では説明されていません
オジー

回答:


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

「リフティング」の意味がわからないが、作成時に値をコンテナ化する場合は、そうだ。
Elizabeth Mattijsen、

@Holliお返事ありがとうございます。それが問題を解決するかどうかはわかりません。あなたの答えは、私が理解していると思うコンテナの可変性に焦点を当てています。私が理解していないのは、最初の例ではプッシュされたコンテナー$ i-またはそれ以上:値-がプッシュ後に更新されるのに対し、2番目の例ではプッシュされたコンテナーの値が現時点で静的に関連付けられたままである理由ですプッシュの。最初の例は私にはある程度意味があります(コンテナーはIntオブジェクトへのポインター-> Intforループで置き換えられます->コンテナーはnewを指しますInt)が、2番目の例はそうではありません。
オジー

@Holli質問を明確にするようにします。
オジー

3

私の上の質問をしばらく遊んで考えた後、私は答えを賭けます...それは私の側での純粋な推測ですので、それがそうであり、あなたがたまたま知っているなら、それはナンセンスだと言ってください。なぜ...

最初の例で$iは、forループの字句スコープの外で定義されています。したがって、$iループとその反復から独立して存在します。$iループ内から参照された場合、$i影響を受けるのは1つだけです。これ$iがにプッシュされ@b、後でループの内容が変更されます。

2番目の例で$iは、forループの字句スコープ内で定義されています。@timotimoが指摘したように、指摘されたブロックは、サブルーチンのように、反復ごとに呼び出されます。$iしたがって、各反復に対して新たに宣言され、それぞれのブロックにスコープされます。$iがループ内で参照される場合、参照はブロック反復固有のものです$i。これは通常、それぞれのループ反復が終了すると存在しなくなります。ただし、ある時点$iでにプッシュされるため、反復の終了後、ガベージコレクター@cはブロック反復固有の$i保持値への参照を2削除できません。それは存在し続けます...しかし、それでも$i後の反復とは異なります。


@raiphありがとう。私がやります。おそらく、私よりも洞察力のある人が答えを適切に(再)言い換えることができるでしょう。いずれにせよ、自分の答えを(推測するのではなく)知っている人が確認(または改善)するまで、自分の答えを正解として受け入れません。
オジー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.