Gibt es eine Alternative für das F # "seq" -Konstrukt in Erlang? Zum Beispiel kann ich in F # eine O (1) Speicherintegrationsfunktion schreiben
%Vor%In Erlang habe ich eine Implementierung, die Listen verwendet und daher eine O (n) Speicheranforderung hat, die für eine solche einfache Funktion inakzeptabel ist,
%Vor%Disclaimer: Das Folgende ist unter der Annahme geschrieben, dass Erlang die Mutation komplett verbietet, worüber ich mir nicht sicher bin, weil ich Erlang nicht gut genug kenne.
Seq ist intern mutationsbasiert. Es behält den "aktuellen Zustand" bei und mutiert es bei jeder Iteration. Wenn Sie also eine Iteration ausführen, erhalten Sie den "next value", aber Sie erhalten auch einen Nebeneffekt, nämlich dass der interne Status des Enumerators sich geändert hat und Sie bei der nächsten Iteration einen anderen "next value" erhalten. , und so weiter. Dies ist in der Regel gut mit funktionell aussehenden Verständnis bedeckt, aber wenn Sie jemals mit IEnumerator
direkt arbeiten würden, werden Sie die Unreinheit mit bloßem Auge sehen.
Eine andere Möglichkeit, darüber nachzudenken, ist, dass Sie bei einer "Sequenz" zwei Ergebnisse erhalten: "Nächster Wert" und "Rest der Sequenz", und dann wird "Rest der Sequenz" Ihre neue "Sequenz" und Sie kann den Prozess wiederholen. (und die ursprüngliche "Sequenz" ist für immer weg)
Diese Gedankenrichtung kann direkt in F # ausgedrückt werden:
%Vor% Bedeutung: "Eine Lazy-Sequenz ist eine Funktion, die, wenn sie angewendet wird, ihren Kopf und ihren Schwanz zurückgibt, wobei der Schwanz eine andere Lazy-Sequenz ist". MySeq of
ist enthalten, damit der Typ nicht unendlich wird.
(Entschuldigung, ich werde F # benutzen, ich kenne Erlang nicht gut genug; ich bin mir sicher, dass du übersetzen kannst)
Aber wenn wir sehen, wie Sequenzen normalerweise endlich sind, sollte das Ganze optional sein:
%Vor%Angesichts dieser Definition können Sie einige Konstruktoren trivial machen:
%Vor%Karte und Falz sind auch trivial:
%Vor% Und von fold
kannst du alles erstellen, was nicht selbst eine Lazy-Sequenz ist. Aber um faule Sequenzen zu erstellen, brauchen Sie eine "rollende Falte" (manchmal "Scan" genannt):
So verwenden Sie es:
%Vor%Und abschließend möchte ich hinzufügen, dass die auf Mutation basierende Lösung fast immer leistungsfähiger ist (alle Dinge sind gleich), zumindest ein bisschen. Selbst wenn Sie den alten Zustand sofort wegwerfen, während Sie neu berechnen, muss die Erinnerung noch zurückgewonnen werden, was wiederum ein Performance-Hit ist. Die Vorteile der Unveränderbarkeit beinhalten keine Leistungsoptimierung.
Aktualisierung:
Hier ist mein Riss bei Erlang Version. Denken Sie daran, dass dies der allererste Code ist, den ich jemals in Erlang geschrieben habe. Daher bin ich mir sicher, dass es bessere Möglichkeiten gibt, dies zu kodieren, und dass dafür bereits eine Bibliothek vorhanden sein muss.
Verwendung:
%Vor%Dies wird nicht getestet, aber es ist eine Möglichkeit, dies zu tun.
Die Idee besteht darin, die Liste in einen Prozess umzuwandeln, der den nächsten Wert liefert, wenn er gefragt wird. Sie können die Idee leicht verallgemeinern, wenn Sie dies tun müssen.
Alternativ können Sie eine Entfaltung schreiben, die dann die Liste ein Stück nach der anderen entfaltet und diese als Eingabe für den generischen Prozessor verwendet.
Eine andere Möglichkeit ist die Implementierung von Lazy Streams, basierend auf der Idee, dass Expr
um fun () -> Expr end
verzögert werden kann, am besten als -define(DELAY(X), fun() -> X end).
als Makro und dann zusammen mit -define(FORCE(X), X()).
Die auf DELAY / FORCE basierende Lösung ist so etwas wie:
%Vor%Aber nicht getestet.
Die gängigste Methode zum Erstellen einer speicherstabilen Verarbeitung besteht darin, die rekursive Tail-Funktion zu definieren. Zum Beispiel:
%Vor% Aber es sieht nicht klar aus ... Ich hatte das gleiche Problem einmal und habe kurze Helfer for
Funktion das erlaubt, ohne Listen zu iterieren:
Leider ist es ein bisschen langsamer als direkte Rekursion:
%Vor%Also sind es ~ 3.03s bis ~ 4.07s - nicht so schlimm.
Ich mag prägnanten Ausdruck, deshalb schlage ich diese vierte Lösung vor (definiert in der Shell, aber es sollte einfach sein, sich an ein Modul anzupassen):
%Vor%Int führt die Auswertungsschleife rekursiv durch, Integral bereitet die Parameter vor dem Aufruf von Int.
vor