Ihre Eingabe enthält eine gerade Anzahl von Elementen:
say elems <1 1 0 2 0 2 1 2 2 2 4 4 3 3>; # 14
Ihr grep
Block verbraucht jedes Mal zwei Elemente:
{$^a eq $^b}
Wenn Sie also ein Element hinzufügen oder entfernen, wird der Fehler angezeigt, den Sie erhalten, wenn der Block für das einzelne Element ausgeführt wird, das am Ende übrig bleibt.
Es gibt viele Möglichkeiten, Ihr Problem zu lösen.
Sie haben aber auch nach der Möglichkeit gefragt, Überlappungen zuzulassen, sodass Sie beispielsweise zwei (2 2)
Unterlisten erhalten, wenn die Sequenz 2 2 2
angetroffen wird. Und in ähnlicher Weise möchten Sie vermutlich zwei Übereinstimmungen sehen, nicht Null, mit Eingaben wie:
<1 2 2 3 3 4>
Daher werde ich mich auf Lösungen konzentrieren, die sich auch mit diesen Problemen befassen.
Trotz der Verengung des Lösungsraums, um die zusätzlichen Probleme zu lösen, gibt es immer noch viele Möglichkeiten, Lösungen funktional auszudrücken.
Eine Möglichkeit, die nur ein bisschen mehr Code an Ihr Ende anfügt:
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
Die .rotor
Methode konvertiert eine Liste in eine Liste von Unterlisten mit derselben Länge. Zum Beispiel say <1 2 3 4> .rotor: 2
Anzeigen ((1 2) (3 4))
. Wenn das Längenargument ein Paar ist, ist der Schlüssel die Länge und der Wert ein Versatz zum Starten des nächsten Paares. Wenn der Versatz negativ ist, erhalten Sie eine Überlappung der Unterlisten. So wird say <1 2 3 4> .rotor: 2 => -1
angezeigt ((1 2) (2 3) (3 4))
.
Die .flat
Methode "glättet" ihren Invokanten. Zum Beispiel say ((1,2),(2,3),(3,4)) .flat
Anzeigen (1 2 2 3 3 4)
.
Eine vielleicht besser lesbare Möglichkeit, die obige Lösung zu schreiben, besteht darin, die Unterlisten wegzulassen flat
und zu verwenden .[0]
und in sie .[1]
zu indizieren, die zurückgegeben werden von rotor
:
say @s .rotor( 2 => -1 ) .grep: { .[0] eq .[1] }
Siehe auch Elizabeth Mattijsens Kommentar für eine weitere Variation, die für jede Unterlistengröße verallgemeinert wird.
Wenn Sie ein allgemeineres Codierungsmuster benötigen, können Sie Folgendes schreiben:
say @s .pairs .map: { .value xx 2 if .key < @s - 1 and [eq] @s[.key,.key+1] }
Die .pairs
Methode in einer Liste gibt eine Liste von Paaren zurück, wobei jedes Paar jedem der Elemente in seiner Invokantenliste entspricht. Das .key
von jedem Paar ist der Index des Elements in der Invocant-Liste; Das .value
ist der Wert des Elements.
.value xx 2
hätte geschrieben werden können .value, .value
. (Siehe xx
.)
@s - 1
ist die Anzahl der Elemente in @s
minus 1.
Das [eq]
in [eq] list
ist eine Reduzierung .
Wenn Sie eine Textmusterübereinstimmung benötigen, um zu entscheiden, was zusammenhängende gleiche Elemente sind, können Sie die Eingabeliste in eine Zeichenfolge konvertieren, diese mit einem der Übereinstimmungsadverbien abgleichen, die eine Liste von Übereinstimmungen generieren, und dann aus der resultierenden Liste von Übereinstimmungen Ihrer gewünschten zuordnen Ergebnis. So passen Sie Überlappungen an (z. B. 2 2 2
Ergebnisse bei der ((2 2) (2 2))
Verwendung :ov
:
say @s .Str .match( / (.) ' ' $0 /, :ov ) .map: { .[0].Str xx 2 }
2 2 2 2
erstellen, werden 3(2 2)
s gedruckt, was wie erwartet ist.rotor
Ich habe noch nie von der Methode gehört. Ich habe die Methode ursprünglichsquish
entwickelt und überprüft, ob sie Funktionen oder Argumente wie enthält@s.squish(:length 2, :multiple_instances yes)
, aber keine solchen Funktionen hatte und nicht für die Aufgabe geeignet war. Verglichen mit demsquish
,rotor
scheint ziemlich fit. Tatsächlich könnte es sogar das geeignetste für diese Art von Operation sein.