关于Raku中EVAL中哈希绑定的问题
在 EVAL 中绑定散列时,我遇到了一些我不明白的事情。在 EVAL 之外绑定哈希按预期工作。EVAL 中的未绑定哈希按预期工作。但是在 EVAL 内绑定一个散列并不像我期望的那样工作。(我的期望可能是错误的。)这是代码:
这有效:
#!/usr/bin/env raku
class Hash::Test does Associative {
has %.hash;
multi method STORE(@pairs) {
for @pairs -> $pair {
self.STORE: $pair
}
}
multi method STORE(Pair $pair) {
%!hash{$pair.key} = $pair.value;
}
}
no strict;
%hash-test := Hash::Test.new;
%hash-test = foo => 'bar', baz => 'quux';
say %hash-test;
#!/usr/bin/env raku
class Hash::Test does Associative {
has %.hash;
multi method STORE(@pairs) {
for @pairs -> $pair {
self.STORE: $pair
}
}
multi method STORE(Pair $pair) {
%!hash{$pair.key} = $pair.value;
}
}
no strict;
%hash-test := Hash::Test.new;
%hash-test = foo => 'bar', baz => 'quux';
say %hash-test;
输出:
这有效:
#!/usr/bin/env raku
class Foo {
use MONKEY-SEE-NO-EVAL;
method eval(Str $code) {
EVAL $code;
}
}
my $code = q:to/END/;
no strict;
%hash = foo => 'bar', baz => 'quux';
END
Foo.eval: $code;
say %Foo::hash;
#!/usr/bin/env raku
class Foo {
use MONKEY-SEE-NO-EVAL;
method eval(Str $code) {
EVAL $code;
}
}
my $code = q:to/END/;
no strict;
%hash = foo => 'bar', baz => 'quux';
END
Foo.eval: $code;
say %Foo::hash;
输出:
$ ./hash-binding-works.raku
Hash::Test.new(hash => {:baz("quux"), :foo("bar")})
但这不起作用:
#!/usr/bin/env raku
class Hash::Test does Associative {
has %.hash;
multi method STORE(@pairs) {
for @pairs -> $pair {
self.STORE: $pair
}
}
multi method STORE(Pair $pair) {
%!hash{$pair.key} = $pair.value;
}
}
class Foo {
use MONKEY-SEE-NO-EVAL;
method eval(Str $code) {
EVAL $code;
}
}
my $code = q:to/END/;
no strict;
%hash-test := Hash::Test.new;
%hash-test = foo => 'bar', baz => 'quux';
say %hash-test;
END
no strict;
Foo.eval: $code;
say %Foo::hash-test;
输出:
Hash::Test 不是我正在使用的真正类,而是我打高尔夫球的目的。谁能解释一下这里发生了什么?谢谢!
回答
TL;DR 通过隐式声明no strict;
符自动声明包变量。通过绑定到具有相同名称的隐式包符号的隐式词法变量声明符来声明包变量。您的代码破坏了这种绑定,从而破坏了您的代码。要解决它,请换一种方式说同样的话。our
our
my
解决方案
no strict;
没有帮助,所以我们摆脱了它。也一样our
。相反,我们声明一个my
词法变量,用它做我们需要/可以做的一切,然后,在将是EVAL
d的代码的末尾,创建一个包变量并将它绑定到存储在词法中的值。
my $code = q:to/END/;
my %hash is Hash::Test;
%hash = foo => 'bar', baz => 'quux';
OUR::<%hash-test> := %hash;
END
Foo.eval: $code;
say %Foo::hash-test; # Hash::Test.new(hash => {:baz("quux"), :foo("bar")})
惊喜的解释
在no strict;
隐式声明our
变量下没有显式声明符声明的变量:
no strict;
%hash-test = :a;
say MY::<%hash-test>; # {a => True}
say OUR::<%hash-test>; # {a => True}
换句话说,仅上面前两行的净效果相当于:
our %hash-test = :a;
反过来,our
变量隐式声明my
变量并遵循此 SO 中显示的逻辑。所以这段代码:
no script;
%hash-test := ...;
是这样做的:
(my %hash-test := $?PACKAGE.WHO<%hash-test>) := ...;
它创建了一个词法 %hash-test
符号和一个包 %hash-test
符号,并将它们绑定在一起——这种绑定对于our
变量的正常运行是必不可少的——然后立即打破这种基本绑定。
此后,无论您的代码的其余部分做什么,它只对变量的词法 %hash-test
版本进行处理,使包符号版本%hash-test
高而干燥,以便稍后自动激活为空哈希。
正如 jnthn 在我开头链接的 SO 下面的评论中所说:
我们当然可以警告绑定到
our
变量是没有意义的
但目前没有警告。
正如您在下面的评论中所解释的那样,当您尝试使用%hash-test is Hash::Test
编译器时,神秘地决定您已经编写了“连续两个术语”。正如我在评论中所解释的那样,这是由于上述诡计,当您our
使用通常的语法(或隐式地使用no strict;
)声明变量时。
要解决以上所有问题,请忘记no strict;
,忘记使用our
,而是:
- 使用词法来完成设置值的工作;
- 通过创建包符号
OUR::<%hash-test>
并将其绑定到词法的值来结束。