Trovare le sequenze contigue di elementi uguali in un elenco Raku


9

Vorrei trovare le sequenze contigue di elementi uguali (ad es. Di lunghezza 2) in un elenco

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))

Questo codice sembra ok ma quando viene aggiunto un altro 2 dopo la sequenza di 2 2 2o quando uno 2 viene rimosso da esso, dice Too few positionals passed; expected 2 arguments but got 1Come risolverlo? Si prega di notare che sto cercando di trovarli senza usare il forloop, cioè sto cercando di trovarli usando un codice funzionale il più possibile.

Opzionale: nella sezione stampata in grassetto:

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

2 2si vedono più sequenze di . Come stamparli il numero di volte che vengono visti? Piace:

((1 1) (2 2) (2 2) (4 4) (3 3))

Risposte:


9

Ci sono un numero pari di elementi nel tuo input:

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

Il tuo grepblocco consuma due elementi ogni volta:

{$^a eq $^b}

Quindi se aggiungi o rimuovi un elemento otterrai l'errore che otterrai quando il blocco viene eseguito sul singolo elemento rimasto alla fine.


Esistono molti modi per risolvere il tuo problema.

Ma hai anche chiesto l'opzione di consentire la sovrapposizione, quindi, ad esempio, ottieni due (2 2)elenchi secondari quando 2 2 2si incontra la sequenza . E, in modo simile, presumibilmente vuoi vedere due corrispondenze, non zero, con input come:

<1 2 2 3 3 4>

Quindi mi concentrerò su soluzioni che affrontano anche questi problemi.

Nonostante il restringimento dello spazio della soluzione per affrontare i problemi aggiuntivi, ci sono ancora molti modi per esprimere le soluzioni in modo funzionale.


Un modo per aggiungere un po 'più di codice alla fine del tuo:

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

Il .rotormetodo converte un elenco in un elenco di elenchi secondari, ciascuno della stessa lunghezza. Ad esempio, say <1 2 3 4> .rotor: 2visualizza ((1 2) (3 4)). Se l'argomento lunghezza è una coppia, la chiave è la lunghezza e il valore è un offset per l'avvio della coppia successiva. Se l'offset è negativo si ottiene la sovrapposizione dell'elenco secondario. Quindi say <1 2 3 4> .rotor: 2 => -1visualizza ((1 2) (2 3) (3 4)).

Il .flatmetodo "appiattisce" il suo invocante. Ad esempio, say ((1,2),(2,3),(3,4)) .flatvisualizza (1 2 2 3 3 4).

Un modo forse più leggibile per scrivere la soluzione di cui sopra sarebbe omettere flate utilizzare .[0]e .[1]indicizzare negli elenchi secondari restituiti da rotor:

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

Vedi anche il commento di Elizabeth Mattijsen per un'altra variante che si generalizza per qualsiasi dimensione dell'elenco secondario.


Se ti servisse un modello di codifica più generale, potresti scrivere qualcosa del tipo:

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

Il .pairsmetodo in un elenco restituisce un elenco di coppie, ciascuna coppia corrispondente a ciascuno degli elementi nel suo elenco di invocanti. Il .keydi ciascuna coppia è l'indice dell'elemento nell'elenco di invocanti; la .valueè il valore dell'elemento.

.value xx 2avrebbe potuto essere scritto .value, .value. (Vedi xx.)

@s - 1è il numero di elementi in @smeno 1.

L' [eq]ingresso [eq] listè una riduzione .


Se hai bisogno di una corrispondenza del modello di testo per decidere cosa costituisce elementi uguali contigui, potresti convertire l'elenco di input in una stringa, confrontalo con quello usando uno degli avverbi di corrispondenza che generano un elenco di corrispondenze, quindi mappa dall'elenco di corrispondenze risultante al desiderato risultato. Da abbinare a sovrapposizioni (ad es. 2 2 2Risultati in ((2 2) (2 2))uso :ov:

say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }

Funziona abbastanza bene. Quando aggiungo 2 2 s per fare la sequenza 2 2 2 2, stampa 3 (2 2)s che è come previsto. Non rotorho mai sentito parlare del metodo Inizialmente ho escogitato il squishmetodo e verificato se ha caratteristiche o argomenti come @s.squish(:length 2, :multiple_instances yes)ma non aveva tali caratteristiche e non era adatto per l'attività. Rispetto al squish, rotor sembra abbastanza in forma. In realtà potrebbe anche essere il più adatto per questo tipo di operazione.
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)) Devi solo regolare il $sizeper diverse lunghezze di sequenze.
Elizabeth Mattijsen il

Ciao di nuovo @LarsMalmsteen. Per favore LMK se pensi che le due alternative a rotorcui ho aggiunto abbiano indebolito o rafforzato la mia risposta.
raiph,

La versione raffinata della rotorsoluzione, ad esempio, say @s.rotor(2=>-1).grep:{.[0]eq.[1]}è benvenuta perché è entrambe più brevi (da 3 a 5 caratteri a seconda di come vengono contati gli spazi) e sembra comunque decente. Anche le versioni generalizzate senza il rotormetodo sono benvenute perché mostrano come vengono utilizzate alcune stranezze come xxe :ov. Quindi il problema è molto ben risolto :)
Lars Malmsteen il

5

TIMTOWDI!

Ecco un approccio 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))

Grazie per la risposta. Sembra abbastanza a sé stante. La take ($last, $_)parte è un esempio decente sull'uso del gather and takeduo.
Lars Malmsteen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.