什么算作单据的 "outer list"?

What counts as an "outer list" for a Slip?

docs for 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]) 的语义不同。)

这里 list assignment 是否被视为特殊情况,具有与普通 Slip 不同的语义?还是 Slips/Lists/Arrays 的语义使得这一切在没有特殊外壳分配的情况下保持一致?

Slip 是一个列表 valuecan 将其展平为外部序列。

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

1, |(2, 3)

它这样做是因为逗号 ,.

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

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

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

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

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

# 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)) 将现有列表强制转换为单据。
  3. Empty.raku / |(,).raku 打印值,使其可能被评估。
    Empty 是空 Slip 的特定实例,在运行时有一些特殊处理。
  4. |().List 获取列表而不是单据。
    这需要在这里,因为 Slip 是 List 的子类,所以通常它只是 return 本身。

None 其中与将 Slip 展平为列表有关。

请注意,即使是顶部的评论也指出它只是一个普通列表。


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

@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] return 是标量容器,然后 = 将右边的值赋给那个单一容器。

为了使 Slip 变平,赋值需要访问数组本身。它没有的东西。它只能访问单个标量容器。因此它将 Slip 插入到它所拥有的 Scalar 中。 (事实上​​,赋值并不知道标量甚至是数组中的一个成员。)

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

要让它做任何不同的事情,@a[2] 必须 return 一个知道如何将 Slip 压平成数组的代理。


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

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

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

append 和 push 都很好用 slip:

my @a = 1, |(2, 3);     #[1,2,3]
@a.append: |(3,4);      #[1 2 3 3 4]
@a.push: |(3,4);        #[1 2 3 3 4]

相比之下,没有滑动:

@a.append: (3,4);       #[1 2 3 3 4]
@a.push: (3,4);         #[1 2 3 (3 4)]