尝试分配给字符串+角色时,“无法分配给不可变值”

时间:2018-05-29 17:54:04

标签: iterator mixins perl6

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。有什么想法吗?

2 个答案:

答案 0 :(得分:8)

我不认为这条线符合您的想法:

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;

答案 1 :(得分:6)

您的问题标题指向错误。这个答案涵盖了错误以及您提出的其他隐含和明确的问题。

背景

  

失败了,有点令人困惑&#34;无法分配给不可变的值&#34;。

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

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 containerScalar容器通常隐藏自己。如果你问WHAT类型$a是,你实际得到的是当前已分配给标量的值的类型(它的值为#34;包含&#34;)。您需要VAR才能访问容器 BOUND到$ 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

doesfoo角色混合到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

doesfoo角色混合到绑定到$ 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 foomy $b; $b.VAR does foo;的结果相同,因此您会遇到与上述相同的问题。

您对此感到困惑的其他事情

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

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

由于$a VARIABLE仍绑定到Scalar(如上文背景部分所述),因此现在具有DNA角色的VALUE混合在一起与the decision process for uses about whether to call its argument's .iterator method无关。

  

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

.say for $a.iterator;

它确实会调用您DNA角色的.iterator方法。但是,在.iterator角色的self.comb方法返回的DNA末尾有另一个 iterator来电,所以你&#39 ;重新.say该次要.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属性,或者使用=将值分配给容器时使用。{/ p>

但相反,你得到:

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

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

请注意,可以将任意复合容器绑定到@% sigil变量。所以你可以看到事情最终会如何发挥作用。