Encontrar las secuencias contiguas de elementos iguales en una lista Raku

9

Me gustaría encontrar las secuencias contiguas de elementos iguales (por ejemplo, de longitud 2) en una lista

my @s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
say grep {$^a eq $^b}, @s;

# ==> ((1 1) (2 2) (4 4) (3 3))

Este código se ve bien, pero cuando se agrega un 2 más después de la secuencia de 2 2 2o cuando se elimina un 2 , dice Too few positionals passed; expected 2 arguments but got 1¿Cómo solucionarlo? Tenga en cuenta que estoy tratando de encontrarlos sin usar el forbucle, es decir, estoy tratando de encontrarlos usando un código funcional tanto como sea posible.

Opcional: en la sección impresa en negrita:

<1 1 0 2 0 2 1 2 2 2 4 4 3 3>

2 2Se ven múltiples secuencias de . ¿Cómo imprimirlos la cantidad de veces que se ven? Me gusta:

((1 1) (2 2) (2 2) (4 4) (3 3))
Lars Malmsteen
fuente

Respuestas:

9

Hay un número par de elementos en su entrada:

say elems <1 1 0 2 0 2 1 2 2 2 4 4 3 3>; # 14

Tu grepbloque consume dos elementos cada vez:

{$^a eq $^b}

Entonces, si agrega o elimina un elemento, obtendrá el error que obtiene cuando el bloque se ejecuta en el elemento único que queda al final.


Hay muchas formas de resolver su problema.

Pero también preguntó sobre la opción de permitir la superposición, por ejemplo, obtiene dos (2 2)sublistas cuando 2 2 2se encuentra la secuencia . Y, en una línea similar, presumiblemente desea ver dos coincidencias, no cero, con entradas como:

<1 2 2 3 3 4>

Así que me enfocaré en soluciones que también aborden esos problemas.

A pesar de la reducción del espacio de solución para tratar los problemas adicionales, todavía hay muchas formas de expresar soluciones funcionalmente.


Una forma que solo agrega un poco más de código al final del tuyo:

my @s = <1 1 0 2 0 2 1 2 2 2 4 4 3 3>;
say grep {$^a eq $^b}, @s .rotor( 2 => -1 ) .flat

El .rotormétodo convierte una lista en una lista de sublistas, cada una de la misma longitud. Por ejemplo, say <1 2 3 4> .rotor: 2pantallas ((1 2) (3 4)). Si el argumento de longitud es un par, entonces la clave es la longitud y el valor es un desplazamiento para comenzar el siguiente par. Si el desplazamiento es negativo, se superponen las sublistas. Así se say <1 2 3 4> .rotor: 2 => -1muestra ((1 2) (2 3) (3 4)).

El .flatmétodo "aplana" a su invocante. Por ejemplo, say ((1,2),(2,3),(3,4)) .flatpantallas (1 2 2 3 3 4).

Una forma quizás más legible de escribir la solución anterior sería omitir flaty usar .[0]e .[1]indexar en las sublistas devueltas por rotor:

say @s .rotor( 2 => -1 ) .grep: { .[0] eq .[1] }

Vea también el comentario de Elizabeth Mattijsen para otra variación que generaliza para cualquier tamaño de sublista.


Si necesita un patrón de codificación más general, puede escribir algo como:

say @s .pairs .map: { .value xx 2 if .key < @s - 1 and [eq] @s[.key,.key+1] }

El .pairsmétodo en una lista devuelve una lista de pares, cada par correspondiente a cada uno de los elementos en su lista de invocadores. El .keyde cada par es el índice del elemento en la lista de invocantes; el .valuees el valor del elemento.

.value xx 2podría haber sido escrito .value, .value. (Ver xx)

@s - 1es el número de elementos en @smenos 1.

El [eq]en [eq] listes una reducción .


Si necesita una coincidencia de patrón de texto para decidir qué constituye elementos iguales contiguos, puede convertir la lista de entrada en una cadena, hacer coincidir eso con uno de los adverbios de coincidencia que generan una lista de coincidencias, luego asignar desde la lista de coincidencias resultante a la deseada resultado. Para coincidir con superposiciones (por ejemplo, 2 2 2resultados en ((2 2) (2 2))uso :ov:

say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
raiph
fuente
Funciona bastante bien. Cuando agrego 2 2 s para hacer la secuencia 2 2 2 2, imprime 3 (2 2)s, que es como se esperaba. Nunca escuché sobre el método rotor. Inicialmente se me ocurrió el squishmétodo y comprobé si tiene características o argumentos similares, @s.squish(:length 2, :multiple_instances yes)pero no tenía tales características y no era adecuado para la tarea. En comparación con el squish, rotor parece bastante en forma. En realidad, incluso podría ser el más apto para este tipo de operación.
Lars Malmsteen
3
my $size = 2; say <1 1 0 2 0 2 1 2 2 2 4 4 3 3>.rotor( $size => -$size + 1).grep: { [eq] $_ }# ((1 1) (2 2) (2 2) (4 4) (3 3)) Solo necesita ajustar el $sizepara diferentes longitudes de secuencias.
Elizabeth Mattijsen
Hola de nuevo @LarsMalmsteen. Por favor, LMK, si cree que las dos alternativas a las rotorque he agregado han debilitado o fortalecido mi respuesta.
raiph
La versión refinada de la rotorsolución, say @s.rotor(2=>-1).grep:{.[0]eq.[1]}es decir, es bienvenida porque es más corta (de 3 a 5 caracteres dependiendo de cómo se cuentan los espacios) y aún se ve decente. Las versiones generalizadas sin el rotormétodo también son bienvenidas porque muestran cómo se usan algunas peculiaridades como xxy :ov. Así que el problema está muy bien resuelto :)
Lars Malmsteen
5

TIMTOWDI!

Aquí hay un enfoque iterativo usando gather/ take.

say gather for <1 1 0 2 0 2 1 2 2 2 4 4 3 3> { 
    state $last = ''; 
    take ($last, $_) if $last == $_; 
    $last = $_; 
};

# ((1 1) (2 2) (2 2) (4 4) (3 3))
Holli
fuente
Gracias por la respuesta. Eso se ve muy bien por derecho propio. La take ($last, $_)parte es un ejemplo decente sobre el uso del gather and takedúo.
Lars Malmsteen