arrays set perl6 deselect

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 tipo WhateverCode

* 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 hecho Callable 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]) } ]