Verkettungs mit (++)
Vielleicht tief in das ich denke aber, soweit ich verstehen, wenn Sie versuchen, Listen zum Beispiel mit (++)
verketten:
[1, 2, 3] ++ [4, 5]
(++)
muss die gesamte linke Liste durchlaufen. Ein Blick auf die code of (++) macht es umso klarer.
(++) :: [a] -> [a] -> [a]
(++) [] ys = ys
(++) (x:xs) ys = x : xs ++ ys
Somit wäre es wünschenswert, (++)
zu vermeiden, verwenden, da bei jedem Aufruf reverse(xs)++[x]
die Liste wird immer größer (oder kleiner auf der Sicht abhängig. Wie auch immer, hat das Programm einfach eine andere zu durchqueren Liste mit jedem Aufruf)
Beispiel:
Können sagen, ich Reverse implementieren, wie durch Verkettung vorgeschlagen.
reversex ::[Int]->[Int]
reversex [] = []
reversex (x:xs) = reversex(xs)++[x]
Umkehren einer Liste [1, 2, 3, 4] aussehen würde etwas wie folgt aus:
reversex [1, 2, 3, 4]
reversex [2, 3, 4] ++ [1]
reversex [3, 4] ++ [2] ++ [1]
reversex [4] ++ [3] ++ [2] ++ [1]
reversex [] ++ [4] ++ [3] ++ [2] ++ [1]
[] ++ [4] ++ [3] ++ [2] ++ [1]
[4] ++ [3] ++ [2] ++ [1]
[4, 3] ++ [2] ++ [1]
[4, 3, 2] ++ [1]
[4, 3, 2, 1]
Endrekursion die Nachteile Operator (:) !!!
Eine Methode zum Abwickeln von Aufrufstapeln ist das Hinzufügen einer accumulator. (es ist nicht immer möglich ist, nur einen Speicher hinzuzufügen. Aber die meisten der rekursiven Funktionen mit einer Angebot ist primitive recursive und somit in tail recursive functions umgewandelt werden kann.)
Mit Hilfe des Akkumulators ist es möglich zu machen, Dieses Beispiel arbeiten, mit dem Cons-Operator (:)
. Der Akku - ys
in meinem Beispiel - akkumuliert das aktuelle Ergebnis und wird als Parameter weitergegeben. Wegen des Akkumulators sind wir nun in der Lage den cons Operator zu verwenden, um das Ergebnis anzusammeln, indem wir den Kopf unserer ursprünglichen Liste jedes Mal anfügen.
reverse' :: (Ord a) => [a] -> [a] -> [a]
reverse' (x:xs) ys = reverse' xs (x:ys)
reverse' [] ys = ys
Hier ist eine Sache zu beachten.
Der Akku ist ein zusätzliches Argument. Ich weiß nicht, ob Haskell Standardparameter liefert, aber in diesem Fall wäre es schön, weil Sie immer diese Funktion mit einer leeren Liste als der Speicher wie so nennen würde: reverse' [1, 2, 3, 4] []
Es gibt viel Literatur über Schwanz Rekursion und ich bin sicher gibt es viele ähnliche Fragen auf StackExchange/StackOverflow. Bitte korrigieren Sie mich, wenn Sie Fehler finden.
Mit freundlichen Grüßen
EDIT 1:
Will Ness wies einige Links, um wirklich gute Antworten für diejenigen, die interessiert sind:
BEARBEITEN 2:
Ok. Dank dFeuer und seinen Korrekturen glaube ich, ich verstehe Haskell ein bisschen besser.
1.Die $!
ist jenseits meines Verständnisses. In allen meinen Tests schien es Dinge schlimmer zu machen.
2.As dFeuer hingewiesen: Thunk, die die Anwendung von (:)
zu x
und y
ist semantisch identisch x:y
aber mehr Speicher erfolgt. Das ist speziell für den Cons-Operator (und Lazy Constructors) und es gibt keine Notwendigkeit, die Dinge in irgendeiner Weise zu erzwingen.
3. Wenn ich stattdessen SUMUP Ganze Zahlen aus einer Liste eine sehr ähnliche Funktion, strenge Bewertung durch BangPatterns oder die seq
Funktion den Stapel zu starkem Anwachsen verhindern, wenn in geeigneter Weise verwendet. Beispiel:
sumUp' :: (Num a, Ord a) => [a] -> a -> a
sumUp' (x:xs) !y = reverse' xs (x + y)
sumUp' [] y = y
Beachten Sie den Knall vor y. Ich habe es in Ghci ausprobiert und es braucht weniger Speicher.
Als Randbemerkung, können Sie (und sollte) Aufruf ohne Klammern zu verwenden: 'reverse (x: xs) = reverse xs ++ [x]', oder Sie stolpern, wenn Sie mit Funktionen mit mehreren Argumenten arbeiten. –
Rufen Sie keine Funktionen wie 'func (arg)' auf. Das ist ärmlich Haskell. Rufen Sie immer Funktionen wie 'func arg' auf.Code mit Leerzeichen macht den Code sicherer und lesbarer. – AJFarmar