Als ich mich auf die ausgezeichnete Antwort von jbapple bezog replicate
, aber stattdessen replicateA
(auf der aufgebaut replicate
ist) verwendete, kam ich auf Folgendes:
--Unlike fromList, one needs the length explicitly.
myFromList :: Int -> [b] -> Seq b
myFromList l xs = flip evalState xs $ Seq.replicateA l go
where go = do
(y:ys) <- get
put ys
return y
myFromList
(in einer etwas effizienteren Version) ist bereits definiert und wird intern in verwendetData.Sequence
für den Bau von Finger Bäumen , die Ergebnisse der Sorten sind.
Im Allgemeinen ist die Intuition für replicateA
einfach. replicateA
basiert auf der Funktion applicativeTree . applicativeTree
Nimmt ein Stück eines Baumes einer Größe m
und erzeugt einen ausgewogenen Baum, der n
Kopien davon enthält. Die Fälle für n
bis zu 8 (ein Deep
Finger) sind fest codiert. Alles darüber, und es ruft sich rekursiv auf. Das "anwendbare" Element besteht einfach darin, dass es die Konstruktion des Baums mit Threading-Effekten wie im Fall des obigen Codes mit dem Zustand verschachtelt.
Die go
Funktion, die repliziert wird, ist einfach eine Aktion, die den aktuellen Status abruft, ein Element von der Oberseite entfernt und den Rest ersetzt. Bei jedem Aufruf wird die als Eingabe bereitgestellte Liste weiter nach unten verschoben.
Noch einige konkrete Hinweise
main = print (length (show (Seq.fromList [1..10000000::Int])))
Bei einigen einfachen Tests ergab sich ein interessanter Kompromiss bei der Leistung. Die obige Hauptfunktion lief mit myFromList fast 1/3 niedriger als mit fromList
. Auf der anderen Seite myFromList
wird ein konstanter Heap von 2 MB verwendet, während der Standard fromList
bis zu 926 MB verwendet. Diese 926 MB entstehen, wenn die gesamte Liste auf einmal gespeichert werden muss. In der Zwischenzeit ist die Lösung in der myFromList
Lage, die Struktur in einer trägen Streaming-Art und Weise zu konsumieren. Das Problem mit der Geschwindigkeit ergibt sich aus der Tatsache, dass myFromList
etwa doppelt so viele Allokationen (als Folge des Aufbaus / der Zerstörung der Staatsmonade) durchgeführt werden müssen wiefromList
. Wir können diese Zuordnungen beseitigen, indem wir zu einer CPS-transformierten Zustandsmonade übergehen. Dies führt jedoch dazu, dass zu einem bestimmten Zeitpunkt weitaus mehr Speicherplatz zur Verfügung steht, da der Verlust der Faulheit erfordert, die Liste ohne Streaming zu durchlaufen.
Wenn ich dagegen nicht die gesamte Sequenz mit einer Show myFromList
forciere , sondern nur den Kopf oder das letzte Element extrahiere, wird sofort ein größerer Gewinn erzielt - das Extrahieren des Kopfelements erfolgt fast augenblicklich und das Extrahieren des letzten Elements beträgt 0,8s . Währenddessen fromList
kostet das Extrahieren des Kopfes oder des letzten Elements mit dem Standard ~ 2,3 Sekunden.
Dies sind alles Details und sind eine Folge von Reinheit und Faulheit. In einer Situation mit Mutation und wahlfreiem Zugriff stelle ich mir die replicate
Lösung strikt besser vor.
Es wirft jedoch die Frage auf, ob es eine Möglichkeit gibt, eine applicativeTree
solche umzuschreiben , myFromList
die strikt effizienter ist. Ich denke, das Problem ist, dass die anwendbaren Aktionen in einer anderen Reihenfolge ausgeführt werden als der Baum, aber ich habe nicht vollständig durchgearbeitet, wie dies funktioniert oder ob es eine Möglichkeit gibt, dies zu beheben.