arrays - perl6 ¿Cuál es una forma rápida de deseleccionar elementos de una matriz o lista?
set deselect (4)
Para seleccionar varios elementos de una matriz en perl6, es fácil: solo use una lista de índices:
> my @a = < a b c d e f g >;
> @a[ 1,3,5 ]
(b d f)
Pero para deseleccionar esos elementos, tuve que usar Set:
> say @a[ (@a.keys.Set (-) (1,3,5)).keys.sort ]
(a c e g)
Me pregunto si hay una manera más fácil porque las matrices que uso a menudo son bastante grandes.
Aquí hay otra opción:
my @a = < a b c d e f g >;
say @a[@a.keys.grep(none(1, 3, 5))];
Pero en general, las matrices no están optimizadas para este caso de uso. Están optimizados para trabajar con un solo elemento, o todos los elementos, y los sectores proporcionan un acceso directo para (positivamente) seleccionar varios elementos por clave.
Si nos cuenta sobre el caso de uso subyacente, tal vez podamos recomendar una estructura de datos más adecuada.
Dado que el indexador quiere que los elementos se extraigan, podríamos resolver esto convirtiendo la lista de elementos para excluir en una lista de rangos de elementos para extraer. Es decir, dado:
1, 3, 5
Produciríamos algo equivalente a:
0..0, 2..2, 4..4, 6..Inf
Dado:
my @exclude = 1, 3, 5;
Podemos hacer:
-1, |@exclude Z^..^ |@exclude, Inf
Para descomponerlo, comprima
(-1, 1, 3, 5)
con
(1, 3, 5, Inf)
, pero utilizando el operador de rango con puntos finales exclusivos.
Esto da como resultado, para el ejemplo dado:
(-1^..^1 1^..^3 3^..^5 5^..^Inf)
Lo cual es equivalente a los rangos que mencioné anteriormente. Luego pegamos esto en el indexador:
my @a = <a b c d e f g>
my @exclude = 1, 3, 5;
say @a[-1, |@exclude Z^..^ |@exclude, Inf].flat
Lo que da el resultado deseado:
(a c e g)
Este enfoque es O (n + m).
Probablemente funcionará bastante bien si la matriz es larga, pero la cantidad de cosas a excluir es relativamente pequeña, ya que solo produce los objetos
Range
necesarios para la indexación, y luego la indexación por rango está relativamente bien optimizada.
Finalmente, si el
flat
en el exterior se considera problemático, también es posible moverlo hacia adentro:
@a[{ flat -1, |@exclude Z^..^ |@exclude, $_ }]
Lo que funciona porque al bloque se le pasa el número de elementos en
@a
.
Esto puede ser lento para grandes matrices, pero lógicamente es lo más cercano a lo que está buscando:
my @a = <a b c d>;
say (@a ⊖ @a[0,1]).keys; # (c d)
Básicamente es la misma solución que propuso al principio, usando la diferencia establecida, excepto que la está usando en toda la matriz en lugar de en los índices. Además, en algunos casos puede usar el conjunto directamente; Depende de lo que quieras hacer.
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)
Creo que el código del operador se explica por sí mismo si conoce cada una de las construcciones P6 que usa. Si alguien apreciaría una explicación más allá de lo siguiente, hágamelo saber en los comentarios.
Comenzaré con los dos aspectos que generan la llamada a
not-at
.
*
también conocido como
Desde la
página
Whatever
documento
:
Cuando
*
se usa en la posición del término, es decir, como un operando, en combinación con la mayoría de los operadores, el compilador transformará la expresión en un cierre de tipoWhateverCode
*
hecho se usa en lo anterior como un operando.
En este caso, es el argumento izquierdo (correspondiente al parámetro
$elems
) del operador infijo
not-at
que acabo de crear.
La siguiente pregunta es, ¿hará el compilador la transformación?
El compilador decide en función de si el operador tiene un
*
explícito como parámetro correspondiente al argumento
*
.
Si hubiera escrito
*
lugar de
$elems
entonces no habría
not-at
uno de los pocos operadores que quiere manejar directamente el
*
y hacer lo que elija y el compilador lo llamaría directamente.
Pero no lo hice.
Escribí
$elems
.
Entonces el compilador hace la transformación que describiré a continuación.
La transformación crea un nuevo
WhateverCode
alrededor de la expresión que lo encierra y reescribe el
Whatever
como "it", también conocido como el tema también conocido como
$_
.
Entonces en este caso resulta esto:
* not-at (1,3,5)
dentro de esto:
{ $_ not-at (1,3,5) }
¿Qué hace un subíndice?
El
[...]
en
@a[...]
es un subíndice
Positional
(matriz / lista).
Esto impone varios aspectos de evaluación, de los cuales dos importan aquí:
-
"it", también conocido como el tema también conocido como
$_
se establece en la longitud de la lista / matriz. -
Si el contenido del subíndice es
Callable
, se llama.WhateverCode
Callable
generado como se explicó anteriormente es de hechoCallable
por lo que se llama.
Así que esto:
@a[ * not-at (1,3,5) ]
se convierte en esto:
@a[ { $_ not-at [1,3,5] } ]
que se convierte en esto:
@a[ { infix:not-at(7, [1,3,5]) } ]