【问题标题】:Question About Hash Binding in EVAL in Raku关于 Raku 中 EVAL 中的哈希绑定的问题
【发布时间】:2021-02-18 21:35:17
【问题描述】:

在 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;

输出:

$ ./hash-binding-works.raku 
Hash::Test.new(hash => {:baz("quux"), :foo("bar")})

这行得通:

#!/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-EVAL-works.raku 
{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-EVAL-does-not-work.raku 
Hash::Test.new(hash => {:baz("quux"), :foo("bar")})
{}

Hash::Test 不是我真正使用的类,而是我打高尔夫球的目的。谁能解释这里发生了什么?谢谢!

【问题讨论】:

  • 你为什么使用no strict?如果删除它会发生什么?
  • @ElizabethMattijsen 对我来说,如果我运行最后一个代码但没有 no strict;,我会得到“未声明变量 '%hash-test'”。
  • @raiph,感谢您的关注。是的,那是复制/粘贴失败。我已经更正了。
  • @ElizabethMattijsen,@raiph 说的是正确的。我使用它来与最后一个不起作用的代码块保持一致。由于 EVAL 无法在周围范围内创建词法,因此我无法使用 my

标签: class hash binding eval raku


【解决方案1】:

TL;DR no strict; 通过隐式 our declarator 自动声明 变量。 our 通过一个隐式的my lexical 变量声明器声明包变量,该变量声明器绑定到同名的隐式包符号。您的代码破坏了该绑定,从而破坏了您的代码。要解决它,请用另一种方式说同样的话。

解决方案

no strict; 没有帮助,所以我们摆脱了它。 our 也是如此。相反,我们声明一个my lexical 变量,做我们需要/可以做的一切,然后,在代码的末尾EVALd,创建一个包变量并绑定它的值存储在词法中。

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 变量并遵循this SO 中显示的逻辑。所以这段代码:

no script;
%hash-test := ...;

正在这样做:

(my %hash-test := $?PACKAGE.WHO<%hash-test>) := ...;

创建一个 lexical %hash-test 符号和一个 package %hash-test 符号,并绑定它们——该绑定是必要的 our 变量的正常运行 -- 然后立即打破该基本绑定。

此后,无论您的代码的其余部分做什么,它只对变量的 lexical %hash-test 版本执行,留下 @987654346 的 package 符号版本@high and dry 以便它稍后自动激活为空哈希。


正如 jnthn 在我一开始链接的 SO 下面的评论中所说:

我们当然可以警告绑定到 our 变量是没有意义的

但目前没有警告。


正如您在下面的评论中解释的那样,当您尝试使用 %hash-test is Hash::Test 时,编译器神秘地认为您已经编写了“连续两个术语”。正如我在评论中解释的那样,这是由于上述诡计,当您使用通常的语法(或隐式使用 no strict;)声明 our 变量时。


要解决以上所有问题,请忘记no strict;,忘记使用our,而是:

  • 使用词法来完成设置值的工作;

  • 最后,使用OUR::&lt;%hash-test&gt; 创建包符号并将其绑定到词法的值。

【讨论】:

  • 在最后一个示例中,我最初尝试这样做:%hash-test is Hash::Test = foo =&gt; 'bar', baz =&gt; 'quux'; 但得到Two terms in a row at /development/raku/VTS-Template/EVAL_0:2 ------&gt; %hash-test⏏ is Hash::Test = foo =&gt; 'bar', baz =&gt; 'q expecting any of: infix infix stopper statement end statement modifier statement modifier loop 我是否正确假设is 在引擎盖下绑定?
  • is 是一个 trait 例程,不同的 trait 做不同的事情。我不知道变量上的is 在后面跟着一个类或角色时在幕后做了什么,但我知道它在编译时对Variable 类型的对象执行此操作,这是不一样的人们通常认为的变量。但是您遇到的错误与此无关。错误是因为我在回答中显示的内容。这就像你写了(my %hash-test := $?PACKAGE.WHO&lt;%hash-test&gt;) is Hash::Test = ...,这是连续两个词。
猜你喜欢
  • 2021-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-19
  • 2010-10-07
  • 1970-01-01
  • 1970-01-01
  • 2020-03-28
相关资源
最近更新 更多