lis*_*tor 9 arrays set perl6 deselect raku
要在perl6中从数组中选择多个元素,这很容易:只需使用索引列表即可:
> my @a = < a b c d e f g >;
> @a[ 1,3,5 ]
(b d f)
Run Code Online (Sandbox Code Playgroud)
但是要取消选择这些元素,我必须使用Set:
> say @a[ (@a.keys.Set (-) (1,3,5)).keys.sort ]
(a c e g)
Run Code Online (Sandbox Code Playgroud)
我想知道是否有更简单的方法,因为我使用的数组通常很大?
sub infix:<not-at> ($elems, @not-ats) {
my $at = 0;
flat gather for @not-ats -> $not-at {
when $at < $not-at { take $at++ xx $not-at - $at }
NEXT { $at++ }
LAST { take $at++ xx $elems - $not-at - 1 }
}
}
my @a = < a b c d e f g >;
say @a[ * not-at (1, 3, 5) ]; # (a c e g)
Run Code Online (Sandbox Code Playgroud)
如果您知道它使用的每个P6构造,我认为操作员代码都是不言自明的。如果有人喜欢以下内容的解释,请在评论中告诉我。
我将从生成对的调用的两个方面开始not-at
。
*
又名 Whatever
当
*
在术语位置(即作为操作数)与大多数运算符结合使用时,编译器会将表达式转换为类型的闭包WhateverCode
*
确实在上面用作操作数。在这种情况下,它就是我刚刚创建$elems
的infix not-at
运算符的左参数(与参数相对应)。
下一个问题是,编译器会进行转换吗?编译器根据运算符是否具有*
与参数相对应的显式参数作为决定*
。如果我写的*
不是$elems
这样,那将使not-at
少数几个想要直接处理*
和执行其选择操作的运算符之一,而编译器将直接调用它。但是我没有。我写了$elems
。因此,编译器将执行下面将要描述的转换。
转换WhateverCode
围绕包围的表达式构建了一个新对象,Whatever
并将主题重写为“ it”(也称为主题)$_
。因此,在这种情况下,它将变为:
* not-at (1,3,5)
Run Code Online (Sandbox Code Playgroud)
到这个:
{ $_ not-at (1,3,5) }
Run Code Online (Sandbox Code Playgroud)
[...]
下标是做什么的的[...]
在@a[...]
是Positional
(阵列/列表)下标。这强加了几个评估方面,其中两个问题在这里:
主题又称为“它”,即$_
设置为列表/数组的长度。
如果下标的内容为,Callable
则将其调用。如上所述的WhateverCode
生成确实是一个Callable
被调用的。
所以这:
@a[ * not-at (1,3,5) ]
Run Code Online (Sandbox Code Playgroud)
变成这个:
@a[ { $_ not-at [1,3,5] } ]
Run Code Online (Sandbox Code Playgroud)
变成这样:
@a[ { infix:not-at(7, [1,3,5]) } ]
Run Code Online (Sandbox Code Playgroud)
给定索引器要提取的元素,我们可以通过将要排除的元素列表变成要提取的元素范围列表来解决此问题。也就是说,给定:
1, 3, 5
Run Code Online (Sandbox Code Playgroud)
我们将产生相当于:
0..0, 2..2, 4..4, 6..Inf
Run Code Online (Sandbox Code Playgroud)
鉴于:
my @exclude = 1, 3, 5;
Run Code Online (Sandbox Code Playgroud)
我们能做的:
-1, |@exclude Z^..^ |@exclude, Inf
Run Code Online (Sandbox Code Playgroud)
要打破它,拉链(-1, 1, 3, 5)
用(1, 3, 5, Inf)
,但使用范围运算符独家端点。对于给定的示例,结果为:
(-1^..^1 1^..^3 3^..^5 5^..^Inf)
Run Code Online (Sandbox Code Playgroud)
这相当于我上面提到的范围。然后,将其粘贴到索引器中:
my @a = <a b c d e f g>
my @exclude = 1, 3, 5;
say @a[-1, |@exclude Z^..^ |@exclude, Inf].flat
Run Code Online (Sandbox Code Playgroud)
给出所需的结果:
(a c e g)
Run Code Online (Sandbox Code Playgroud)
这种方法是O(n + m)。如果数组很长,它可能会工作得很好,但是要排除的事物数量相对较少,因为它只产生Range
索引所需的对象,然后按范围进行索引的优化比较好。
最后,如果flat
认为外部麻烦,也可以将其移入内部:
@a[{ flat -1, |@exclude Z^..^ |@exclude, $_ }]
Run Code Online (Sandbox Code Playgroud)
之所以有效,是因为传递了块中的元素数量@a
。
这是另一个选择:
my @a = < a b c d e f g >;
say @a[@a.keys.grep(none(1, 3, 5))];
Run Code Online (Sandbox Code Playgroud)
但总而言之,数组并未针对此用例进行优化。它们针对使用单个元素或所有元素进行了优化,并且切片为快捷键(通过键)选择(肯定)几个元素提供了捷径。
如果您告诉我们基本的用例,也许我们可以推荐一个更合适的数据结构。