Wenn Sie bereits eine Version zwingend notwendig haben, können Sie follow a set of small steps to refector to a recursive implementation.
Rekursion
Während ich weiß nicht, was Ihre Imperativ Version aussieht, hier eine rekursive Version:
let pack xs =
let rec imp acc = function
| [] -> acc
| h::t ->
match acc with
| [] -> imp [(h, 1)] t
| (i, count) :: ta ->
if h = i
then imp ((i, count + 1) :: ta) t
else imp ((h, 1) :: (i, count) :: ta) t
xs |> imp [] |> List.rev
Diese Funktion den Typ 'a list -> ('a * int) list when 'a : equality
hat. Es verwendet eine private 'Implementierungsfunktion' imp
, um die Arbeit zu erledigen. Diese Funktion ist rekursiv und führt einen Akkumulator (acc
) durch. Dieser Akkumulator ist die Ergebnisliste vom Typ ('a * int) list
.
Wenn der Speicher-Liste leer ist, wird der Kopf der ursprünglichen Liste (h
) sowie die Zählung 1
wird als Tupel als das einzige Element des aktualisierten Speichers erzeugt und die imp
Funktion wird rekursiv aufgerufen, mit dieser aktualisierte Akku. Wenn der Akkumulator bereits mindestens ein Element enthält, wird das Element über die Mustererkennung extrahiert, und das Element in diesem Tupel (i
) wird mit h
verglichen. Wenn h = i
, wird der Akkumulator aktualisiert; andernfalls wird ein neues Tupel auf acc
geschrieben. In beiden Fällen wird jedoch imp
rekursiv mit dem neuen Akkumulator aufgerufen.
Sie können es nennen mit einer Liste äquivalent zu Ihrem ursprünglichen Tupel wie folgt aus:
> pack [1; 2; 2; 3; 2; 2; 2; 4];;
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Falten
Sobald Sie eine rekursive Version haben, haben Sie oft das Rezept für eine Version ein mit falten. In diesem Fall, da die obige pack
Funktion den Akkumulator am Ende umkehren muss (unter Verwendung von List.rev
), ist ein rechter Falz am besten geeignet.In F # ist dies mit der List.foldBack
Funktion in integrierten getan:
let pack' xs =
let imp x = function
| (i, count) :: ta when i = x -> (i, count + 1) :: ta
| ta -> (x, 1) :: ta
List.foldBack imp xs []
In diesem Fall ging die Funktion List.foldBack
ist ein bisschen zu komplex als eine anonyme Funktion zu übergeben, so wählte ich es als zu definieren, private innere Funktion. Es entspricht der rekursiven imp
-Funktion, die von der obigen pack
-Funktion verwendet wird, aber Sie werden bemerken, dass es sich nicht rekursiv aufrufen muss. Stattdessen muss nur der neue Wert für den Akkumulator zurückgegeben werden.
Das Ergebnis ist das gleiche:
> pack' [1; 2; 2; 3; 2; 2; 2; 4];;
val it : (int * int) list = [(1, 1); (2, 2); (3, 1); (2, 3); (4, 1)]
Schöne Antwort. Ein paar kleine Änderungen können meiner Meinung nach noch deutlicher werden. Sie können 'let imp x acc = match acc ...' durch 'let imp x = function ...' ersetzen, um zu vermeiden, dass Sie einen Bezeichner benennen, den Sie sofort destrukturieren. Noch wichtiger ist, dass Sie die Reihenfolge der Pattern-Match-Fälle wechseln und eine 'when'-Klausel verwenden können, um die' [] '- und' x <> i'-Fälle zu vereinheitlichen: '| (i, Zählung) :: ta, wenn i = x -> (i, Zählung + 1) :: ta | ta -> (x, 1) :: ta'. – kvb
@ kvb Das sind sehr schöne Vereinfachungen! Vielen Dank. Antwort aktualisiert –