多变量初始化中使用的绑定运算符仅在变量也在同一行中声明时才起作用

Binding operator used in multi-variable initialization is working only when variables are also declared in the same line

在下面的例子中:

my $i1 = 1;
my $i2 = 2;

my ($v1, $v2);

($v1, $v2) := ($i1, $i2);

say $v1;
say $v2;

代码抛出编译错误:

===SORRY!=== Error while compiling ...
Cannot use bind operator with this left-hand side
at ...:8
------> ($v1, $v2) := ($i1, $i2)<HERE>;

当我将声明和绑定放在一行时,它 运行 成功了:

my $i1 = 1;
my $i2 = 2;

my ($v1, $v2) := ($i1, $i2);

say $v1;  # 1
say $v2;  # 2

$i1 = 11;
$i2 = 22;

say $v1;  # 1
say $v2;  # 2

尽管如此,最后一个成功的示例表明变量 $v1、$v2 未绑定到变量 $i1、$i2,而是已分配了各自的值。

似乎并没有实际的绑定,而是一个简单的赋值!

有没有人解释这种行为背后的机制以及为什么会这样,我们还必须在同一行中“声明”变量,以便代码 运行?

在第一种情况下,它只是说您无法在创建后绑定该列表。 当您将一个列表绑定到另一个列表时,您并不是在绑定每个变量,而是在绑定它的容器; := 将其左侧绑定到右侧,使它们实际上是同一事物;如果它们不是一开始的东西,你以后就不能这样做了。

因此,如果您想将一个容器绑定到另一个容器,您实际上必须自行声明并将其绑定到您想要绑定的任何内容。

这也适用于第二种情况。它使 列表 ($v1, $v2) 与右侧相同。这可能会引发错误,但它不会绑定每个单独的容器。

不是在同一行声明。

它是关于在声明中绑定,而不是绑定到列表。


声明中的绑定和赋值产生与常规绑定或赋值不同的代码。

my \abc = 'def';

abc  = 5; # ERROR: Cannot modify an immutable Str
\abc = 5; # ERROR: Cannot modify an immutable Capture


for ^3 {
  state $i = 0;
  say ++$i; # 1␤2␤3␤
}

for ^3 {
  state $i;
  $i = 0;
  say ++$i; # 1␤1␤1␤
}

特别是我认为您的代码并没有像您认为的那样工作。
要么就是这样,要么就是你的期望落空了。

my $i1 = 1;
my $i2 = 2;

my ($v1, $v2) := ($i1, $i2);

say $v1; # 1
say $v2; # 2

$i2 = 3;
say $v2; # 2

# $v2 = 3; # ERROR: Cannot assign to a readonly variable or a value

它不将变量绑定到其他变量,它绑定到它们内部的当前值。

如果那确实是您想要的,您可以绑定到 Signature 对象。

my $i1 = 1;
my $i2 = 2;

my ($v1, $v2);

:($v1, $v2) := ($i1, $i2); # <--

say $v1; # 1
say $v2; # 2

$i2 = 3;
say $v2; # 2

# $v2 = 3; # ERROR: Cannot assign to a readonly variable or a value

绑定存在的全部原因是将一个变量绑定到另一个变量。

sub var-name ( $a is rw ){ # <-- Bind $a to the incoming variable
  $a.VAR.name;
}

my $foo;
say var-name $foo; # $foo

在上面的例子中 $a 绑定到 $foo
你对 $foo 所做的任何事情也会发生在 $a 和 vice-versa.

您也可以使用绑定运算符手动执行此操作 :=

my ($a,$b,$c);

{
  my $v := $a;
  say $v.VAR.name; # $a

  $v := $b;
  say $v.VAR.name; # $b

  $v := $c;
  say $v.VAR.name; # $c
}

当你绑定一些东西时,它会把左边的东西指向右边的东西。

my ($a,$b,$c,$v);

$v := ($a,$b,$c);

$v 变量现在绑定到包含变量 $a$b$c.

的列表

请注意,该列表不是变量内部的值,而是实际变量本身。

say $v[0].VAR.name; # $a
say var-name $v[1]; # $b

say $v.^name; # List

$v[2] = 7;
say $c; # 7

当您尝试将一个列表绑定到另一个列表时,您正在尝试覆盖左侧的列表。不是List里面的变量。

即使你做到了,那也是毫无意义的。它唯一要做的就是让左边的列表更快地收集垃圾。

当您看到 my $a 时,发生了一些事情。

  1. 创建了一个指针,并将其添加到命名空间中 $a 的名称下。
    (反正足够接近)
  2. 标量对象已创建。它的属性 name 设置为 $a
  3. 该指针开始指向标量。

几乎变量的行为实际上是该标量容器的行为。

除了一个。

当您使用绑定运算符 := 时,您正在使用该指针并将其指向一个新事物。

$a := $b;

# pseudo code
$a.POINTER = $b.POINTER;

列表不是指针。 (虽然可以指出。)

(注意声明也涉及到标量的初始赋值。)


通过赋值 = 变量或值有机会选择发生什么。

如果您分配给数组,则每个标量容器都会分配给右侧的每个值。

my @a;
@a = 0,1,2,3;

my $v := @a[4];
$v = 4;

say @a.raku; # [0, 1, 2, 3, 4]



sub assign ( Positional $dest is raw, Positional $source is raw ){
  #for $source.keys -> $index {
  #  my $v := $dest[$index];
  #  $v = $source[$index];
  #}
  $dest.STORE($source)
}

@a = ();
assign @a, (5,6,7,8);

say @a.raku; # [5, 6, 7, 8]

当你使用赋值 = 时,你基本上是在调用左边的 STORE 方法。它可以决定会发生什么。 (实际上比这要复杂一点。)

在数组或列表的情况下,它遍历元素,依次分配每个元素。 (假设该元素实际上是一个标量容器。)

 my ($a,$b);

 ($a,$b) = 1,2;

 ($a,$b).STORE( (1,2) );

 my $list = ($a,$b);
 $list.STORE( (1,2) );

当你绑定时,你只是简单地覆盖了左边的东西。