什么算作 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,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 是一个列表,它可以展平为外部序列。

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

1,|(2,3)

这样做是因为逗号 ,

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

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

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

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

事实上,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,'$!todo'))
          if nqp::isconcrete(nqp::getattr(self,'$!todo'));
        nqp::bindattr($list,'$!reified','$!reified'))
          if nqp::isconcrete(nqp::getattr(self,'$!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,在本例中为 ListRange)作为索引,您可以将其展平。

@a[2,] = |(3,4); # [Any,Any,3]
@a[2,3] = |(3,3,4]
@a[2..3] = |(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,3);
@a.splice: 2,1,|(3,4);

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

,

append 和 push 都可以很好地使用滑动:

my @a = 1,3);     #[1,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)]

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...