什么算作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 个功能起作用。
|().defined
这是定义的吗?
如果它包含元素是,否则不是。
(Empty绝对是未定义的,但|(,)或者slip()应该定义。这个方法只是说两者都是未定义的。)Slip((1,2))
将现有列表强制转换为 Slip。Empty.raku/|(,).raku
打印值,以便它可能被评估。
Empty是一个空 Slip 的特定实例,它在运行时有一些特殊的处理。|().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,这并不特别。