什么算作Slip的“外部清单”?

Slip的文档提到“Slip 是一个列表,它会自动展平为外部列表(或其他类似列表的容器或可迭代对象)”。基于这个定义,这是完全合理的:

dd my @a = 1, |(2, 3); # OUTPUT: «Array @a = [1, 2, 3]»

但是,我对以下内容感到惊讶:

dd my @b = do {@a[2] := |(3, 4); @a} # OUTPUT: «Array @b = [1, 2, slip(3, 4)]»

我原以为 会变slip(3, 4)平为@b,而不是保持为slip. (也就是说,很惊讶@a[2] := |(3, 4)没有与@a.splice(2, 1, [3, 4]).相同的语义。)

是列表赋值得到治疗作为特殊情况在这里,有不同的语义比正常单吗?或者是否有关于 Slips/Lists/Arrays 的语义使这一切都保持一致而无需特殊大小写分配?

回答

Slip 是一个列表可以展平为外部序列。

所以下面产生了一个扁平化的列表。

1, |(2, 3)

它这样做是因为逗号,

如果将该列表插入到给定位置的数组中,则是将该列表插入到单个给定位置的数组中。

@a[0] = 1, |(2, 3); # [(1,2,3),]

如果你插入一个 Slip 也会发生同样的事情,因为 Slip 只是 List 的一个子类。

@a[0] = |(2, 3); # [slip(2,3),]

事实上,Slip 几乎完全只是一个列表。这是来自 Rakudo 的 Slip 代码。

# A Slip is a kind of List that is immediately incorporated into an iteration
# or another List. Other than that, it's a totally normal List.
my class Slip { # is List

    # XXX this makes an empty Slip undefined?
    multi method defined (Slip:D: --> Bool:D) { self.Bool }

    multi method Slip(Slip:D:) { self }
    method CALL-ME (+args)     { args.Slip }
    multi method raku(Slip:D: --> Str:D) {
        nqp::if(
          nqp::eqaddr(self,Empty),
          'Empty',
          nqp::stmts(
            (my str $guts = callsame),
            nqp::if(
              nqp::eqat($guts,'$',0), # we're itemized
              nqp::concat('$(slip',nqp::concat(nqp::substr($guts,1),')')),
              nqp::concat('slip',$guts)
            )
          )
        )
    }
    multi method List(Slip:D: --> List:D) {
        my $list := nqp::create(List);
        nqp::bindattr($list,List,'$!todo',nqp::getattr(self,List,'$!todo'))
          if nqp::isconcrete(nqp::getattr(self,List,'$!todo'));
        nqp::bindattr($list,List,'$!reified',nqp::getattr(self,List,'$!reified'))
          if nqp::isconcrete(nqp::getattr(self,List,'$!reified'));
        $list
    }
}

这只会使 4 个功能起作用。

  1. |().defined
    这是定义的吗?
    如果它包含元素是,否则不是。
    Empty绝对是未定义的,但|(,)或者slip()应该定义。这个方法只是说两者都是未定义的。)
  2. Slip((1,2))
    将现有列表强制转换为 Slip。
  3. Empty.raku/|(,).raku
    打印值,以便它可能被评估。
    Empty是一个空 Slip 的特定实例,它在运行时有一些特殊的处理。
  4. |().List
    获取列表而不是单据。
    这需要在这里,因为 Slip 是 List 的子类,所以通常它只会返回自身。

所有这些都与 Slip 扁平化为 List 无关。

请注意,即使顶部的注释也说明它只是一个普通列表。


如果您使用列表(ish,在本例中为 aList或 a Range)作为索引,则可以将其展平。

@a[2,  ] = |(3,4); # [Any, Any, 3]
@a[2, 3] = |(3,4); # [Any, Any, 3, 4]
@a[2..3] = |(3,4); # [Any, Any, 3, 4]

通过使用列表索引,您告诉 Raku@a[…]操作的结果是一个列表,而不是单个值。

这与右值是 Slip 无关。它与右值是 List 的子类有关。


更明确一点。

my $l-value := @a[2]; # contains the actual Scalar object in the array
my $r-value := |(3,4);
$l-value = $r-value;

这与您的代码所做的基本相同

@a[2] = |(3,4);

@a[2] =是两个独立的操作。索引,然后分配

@a[2]返回的标量容器,则=受让人在右侧成奇异容器的值。

为了使 Slip 变平,分配需要访问 Array 本身。它没有的东西。它只能访问单一的标量容器。所以它将 Slip 插入到它确实拥有的标量中。(实际上,赋值并不真正知道标量甚至是数组中的成员。)

当你绑定时,你会做一些稍微不同的事情。问题是绑定应该低于常规分配的级别。所以更有可能只是插入一个点而不是变平。

为了让它做任何不同的事情,它@a[2]必须返回一个知道如何将 Slip 展平为数组的代理。


如果您真的想这样做,请使用splice它,因为它具有对数组的引用。

my @a = 1, |(2, 3);
@a.splice: 2, 1, |(3,4);

同样,由于 Slip,这并不特别。


以上是什么算作Slip的“外部清单”?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>