动态变量、CALLERS、标量和赋值

cod*_*ons 8 variable-assignment rakudo dynamic-variables raku

我最近注意到,在使用赋值的大多数情况下,重新初始化动态变量没有我预期的语义(但是,绑定以我预期的方式工作)。

具体来说,在这段代码中:

sub g {
    my $*i  = CALLERS::<$*i>  // 0;
    my $*a1 = CALLERS::<$*a1> // Array.new;
    my @*a2 = CALLERS::<@*a2> // Array.new;
    $*i++;
    $*a1.push: 'v1';
    @*a2.push: 'v2';
    dd $*i;
    dd $*a1;
    dd @*a2;
}
sub f {
    my $*i = 0;
    my $*a1 = Array.new;
    my @*a2 = Array.new;
    g; g; g;
}
f
Run Code Online (Sandbox Code Playgroud)

我期望输出3, ["v1", "v1", "v1"],["v2", "v2", "v2"]而不是得到1, $["v1", "v1", "v1"], ["v2"]。切换到绑定解决了这个问题,所以我没有试图解决问题——但我非常想了解为什么赋值在这里不起作用。我注意到指向 Array 的 Scalar 有效,但指向 Int 的 Scalar 无效。但无论哪种情况,我都会认为新分配的变量会从 CALLERS 接收值。我对赋值的语义遗漏了什么?

Eli*_*sen 7

我对赋值的语义遗漏了什么?

我认为您所缺少的与动态变量本身没有任何关系。我认为你缺少的是这样一个事实:

my @a = Array.new;
Run Code Online (Sandbox Code Playgroud)

基本上是一个 noop。由于单参数规则,它与以下内容相同:

my @a = ();
Run Code Online (Sandbox Code Playgroud)

这与:

my @a;
Run Code Online (Sandbox Code Playgroud)

所以,在你的例子中sub f,:

my @*a2 = Array.new;
Run Code Online (Sandbox Code Playgroud)

in 只是在动态变量中设置一个空数组。

然后在sub g

my @*a2 = CALLERS::<@*a2> // Array.new;
Run Code Online (Sandbox Code Playgroud)

基本上只是在做(因为单参数规则):

my @*a2;
Run Code Online (Sandbox Code Playgroud)

因此你看不到push你以前做过的es,因为每次调用都是新鲜的。

关于价值$*isub g:也只是增加一个副本的呼叫者$*i的,所以值保持1在每个呼叫。

有效的原因$*a1是容器化停止了单参数规则的扁平化行为。观察两者之间的区别:

sub a(+@a) { dd @a }; a [2,3,4];  # [2,3,4]
Run Code Online (Sandbox Code Playgroud)

和:

sub a(+@a) { dd @a }; a $[2,3,4];  # [[2,3,4],]
Run Code Online (Sandbox Code Playgroud)