【问题标题】:"Cannot assign to immutable value" when trying to assign to a string + role尝试分配给字符串+角色时“无法分配给不可变的值”
【发布时间】:2018-11-08 10:55:50
【问题描述】:

以the example in the Iterable doc page开头

role DNA does Iterable {
  method iterator(){ self.comb.iterator }
};

my @a does DNA = 'GAATCC';
.say for @a; # OUTPUT: «G␤A␤A␤T␤C␤C␤» 

我发现它使用@ 声明很奇怪,所以我将其更改为自然 声明字符串的方式$:

my $a does DNA = 'GAATCC';

但这失败了,因为“不能分配给不可变的值”有点令人困惑。无需当场分配,我们可以这样做:

my $a = 'GAATCC';
$a does DNA;
.say for $a;

这只是将混合留到以后。但这只是打印字符串,没有注意Iterable mixin。让我们明确地调用它:

.say for $a.iterator;

它和以前做的有点像,只是它打印$a.iterator的值,而不实际调用函数:

<anon|69>.new

这看起来像the same thing it's going on in this other question。基线问题是我不明白 Iterable 的真正作用,for 的真正作用以及它何时在某个对象上调用 iterator。有什么想法吗?

【问题讨论】:

    标签: iterator mixins raku


    【解决方案1】:

    我不认为这条线和你想的一样:

    my @a does DNA = 'GAATCC';
    

    同理:

    my @a := [ 'GAATCC', ];
    @a does DNA;
    

    基本上,.comb 调用将数组强制转换为 Str,并将其拆分为字符。


    如果您改为这样做:

    my @a = 'GAATCC' but DNA;
    

    基本相同

    my @a := Seq.new(('GAATCC' but DNA).iterator).Array;
    

    注意@ 变量存储Positional 值而不是Iterable 值。


    你想要的是

    my $a = 'GAATCC' but DNA;
    
    $a.map: &say;
    

    如果您希望能够使用for,则不能使用带有$ sigil 的变量

    my \a = 'GAATCC' but DNA;
    
    .say for a;
    

    您可能希望将Seq list List 等方法添加到DNA。

    role DNA does Iterable {
      method iterator(){
        self.comb.iterator
      }
      method Seq(){
        Seq.new: self.iterator
      }
      method list(){
        # self.Seq.list
        List.from-iterator: self.iterator
      }
    }
    
    my $a = 'GAATCC' but DNA;
    .say for @$a;
    

    【讨论】:

    • 为什么不能使用带有$ sigil 的变量?
    • @jjmerelo 它被标记为item(单数)。
    • 我正在使用它here with the sigil 并没有问题。
    • @jjmerelo 我认为这是因为您创建了一个新类,而不是从Str 派生。我还没有弄清楚如何让它以这种方式工作。我假设有一些方法我必须重写。
    【解决方案2】:

    您的问题的标题指向一个错误。此答案涵盖了该错误以及您提出的其他隐式和显式问题。

    背景

    以有点令人困惑的“无法分配给不可变的值”而失败。

    我认为这是一个错误。让我们从一些有效的代码开始:

    my $a = 42;
    say $a;          # 42
    say WHAT $a;     # (Int)               type of VALUE currently ASSIGNED to $a
    say WHAT VAR $a; # (Scalar)            type of VARIABLE currently BOUND to $a
    $a = 42;         # works fine
    

    在my 声明中$a 绑定到一个新的Scalar container。 Scalar 容器通常会隐藏自己。如果你问WHAT type $a 是,你实际上得到了当前分配给标量的值的类型(它“包含”的值)。您需要VAR 才能访问绑定到$a 的容器。当您将 = 分配给 Scalar 容器时,您会将分配的值复制到容器中。

    role foo {}
    $a does foo;     # changes the VALUE currently ASSIGNED to $a
                     # (NOT the VARIABLE that is BOUND to $a)
    say $a;          # 42                  mixed in `foo` role is invisible
    say WHAT $a;     # (Int+{foo})         type of VALUE currently ASSIGNED to $a
    say WHAT VAR $a; # (Scalar)            type of VARIABLE currently BOUND to $a
    $a = 99; say $a; # 99
    

    does 将foo 角色混合到42 中。您仍然可以分配给 $a,因为它仍然绑定到 Scalar。

    注意does 的这两种用法如何产生截然不同的效果:

    my $a does foo;  # mixes `foo` into VARIABLE bound to $a
    $a does foo;     # mixes `foo` into VALUE assigned to $a
    

    错误

    $a.VAR does foo; # changes VARIABLE currently BOUND to $a (and it loses the 42)
    say $a;          # Scalar+{foo}.new    VALUE currently ASSIGNED to $a
    say WHAT $a;     # (Scalar+{foo})      type of VALUE currently ASSIGNED to $a
    say WHAT VAR $a; # (Scalar+{foo})      type of VARIABLE currently BOUND to $a
    
    $a = 'uhoh';     # Cannot assign to an immutable value
    

    does 将foo 角色混合到绑定到$a 的Scalar 中。似乎带有 mixin 的 Scalar 不再成功地用作容器并且分配失败。

    目前在我看来这像是一个错误。

    my $b does foo;  # BINDS mixed in VARIABLE to $b
    $b = 'uhoh';     # Cannot assign to an immutable value
    

    my $b does foo 与my $b; $b.VAR does foo; 的结果相同,因此您会遇到与上述相同的问题。

    其他你感到困惑的事情

    my $a = 'GAATCC';
    $a does DNA;
    .say for $a;
    

    只打印字符串,不关注Iterable mixin。

    因为$a VARIABLE 仍然绑定到Scalar(如上面的背景 部分所述),现在混入DNA 角色的VALUE 与@ 无关987654322@.

    让我们显式调用它...打印$a.iterator 的值,而不实际调用函数:

    .say for $a.iterator;
    

    它确实调用了您的DNA 角色的.iterator 方法。但这在DNA 角色的iterator 方法返回的self.comb 末尾有另一个 .iterator 调用,所以你是.saying 第二个.iterator。

    当今有效的解决方案

    我认为布拉德的回答很好地涵盖了您的大部分选择。

    如果你想使用$ sigil,我认为你漂亮的does DNA gist 与今天的P6 一样好。

    一种可能有一天会奏效的解决方案

    在理想情况下,P6 设计中的所有优点都将在 6.c 和 Perl 6 的 Rakudo 编译器实现中得到充分体现。也许这将包括编写此代码并获得所需内容的能力:

    class DNA is Scalar does Iterable { ... }
    my $a is DNA = 'GAATCC';
    .say for $a;
    

    ... 代码与 gist 中的代码大致相同,只是 DNA 类将是一个标量容器,因此 new 方法将改为 STORE 方法或类似方法当使用= 将值分配给容器时,会将传递的值分配给$!value 属性或类似属性。

    但相反,你会得到:

    is trait on $-sigil variable not yet implemented. Sorry.
    

    因此,您今天最接近理想的 = 'string' 以更改 $a 是使用 := DNA.new('string') 绑定,就像您在要点中所做的那样。

    请注意,您可以将任意复合容器绑定到@ 和% sigil 变量。所以你可以看到事情最终应该如何运作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-15
      • 2013-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-01
      相关资源
      最近更新 更多