Erstens sagt Real World Haskell , das ich lese, dass man es niemals benutzen foldl
und stattdessen benutzen soll foldl'
. Also vertraue ich ihm.
Aber ich bin trüb , wenn die Verwendung foldr
vs. foldl'
. Obwohl ich die Struktur ihrer Funktionsweise vor mir sehen kann, bin ich zu dumm, um zu verstehen, wann "was besser ist". Ich denke, es scheint mir, dass es eigentlich keine Rolle spielen sollte, welche verwendet wird, da beide die gleiche Antwort liefern (nicht wahr?). Tatsächlich stammen meine früheren Erfahrungen mit diesem Konstrukt aus Rubys inject
und Clojures reduce
, die keine "linken" und "rechten" Versionen zu haben scheinen. (Nebenfrage: Welche Version verwenden sie?)
Jede Einsicht, die einer klugen Art wie mir helfen kann, wäre sehr dankbar!
quelle
fodlWhile
); 2. Konvertieren Sie es in einen linken Scan (scanl
) und stoppen Sie diesen mitlast . takeWhile p
oder ähnlichem. Äh und 3. verwendenmapAccumL
. :)foldl
, dass es sein Gesamtergebnis sammelt und es erst erzeugt, wenn alle Arbeiten abgeschlossen sind und keine weiteren Schritte mehr ausgeführt werden müssen. Also zBfoldl (flip (:)) [] [1..3] == [3,2,1]
, alsoscanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... IOWfoldl f z xs = last (scanl f z xs)
und unendliche Listen haben kein letztes Element (was im obigen Beispiel selbst eine unendliche Liste von INF bis 1 wäre).foldr
sieht aus wie das:foldl
sieht aus wie das:Kontext: Falten Sie im Haskell-Wiki
quelle
Ihre Semantik unterscheidet sich, so dass Sie nicht einfach austauschen können
foldl
undfoldr
. Der eine faltet die Elemente von links zusammen, der andere von rechts. Auf diese Weise wird der Operator in einer anderen Reihenfolge angewendet. Dies ist für alle nicht assoziativen Operationen wie die Subtraktion von Bedeutung.Haskell.org hat einen interessanten Artikel zu diesem Thema.
quelle
Kurz gesagt,
foldr
ist besser, wenn die Akkumulatorfunktion beim zweiten Argument faul ist. Lesen Sie mehr im Stack Overflow des Haskell-Wikis (Wortspiel beabsichtigt).quelle
Der Grund, warum 99% aller Verwendungen
foldl'
bevorzugt werden,foldl
ist, dass es für die meisten Verwendungen auf konstantem Raum ausgeführt werden kann.Übernimm die Funktion
sum = foldl['] (+) 0
. Wennfoldl'
verwendet wird, wird die Summe sofort berechnet, so die Anwendungsum
auf eine unendliche Liste lief nur für immer, und höchstwahrscheinlich in konstantem Raum (wenn Sie Dinge wie verwendenInt
s,Double
s,Float
s.Integer
S mehr als konstanter Raum verwendet, wenn die Anzahl wird größer alsmaxBound :: Int
).Mit
foldl
wird ein Thunk aufgebaut (wie ein Rezept, wie man die Antwort erhält, das später ausgewertet werden kann, anstatt die Antwort zu speichern). Diese Thunks können viel Platz beanspruchen, und in diesem Fall ist es viel besser, den Ausdruck zu bewerten, als den Thunk zu speichern (was zu einem Stapelüberlauf führt… und Sie zu… oh, egal)Hoffentlich hilft das.
quelle
foldl
nur Konstruktoren auf eines oder mehrere ihrer Argumente anwendet.foldl
tatsächlich die beste Wahl ist? (Wie unendliche Listen, wennfoldr
die falsche Wahl ist, was die Optimierungfoldr
, die falsche Wahl für unendliche Listen zu sein. In der Tat sollten Sie nichtfoldl
oderfoldl'
für unendliche Listen verwenden. Sehen Sie sich das Haskell-Wiki über Stapelüberläufe anBy the way, Ruby
inject
und Clojure istreduce
sindfoldl
(oderfoldl1
, je nachdem , welche Version Sie verwenden). Wenn es in einer Sprache nur eine Form gibt, handelt es sich normalerweise um eine linke Falte, einschließlich Pythonsreduce
, PerlsList::Util::reduce
, C ++accumulate
, C #Aggregate
, Smalltalksinject:into:
, PHPsarray_reduce
, MathematicaFold
usw. Diereduce
Standardeinstellung von Common Lisp ist die linke Falte, es gibt jedoch eine Option für die rechte Falte.quelle
reduce
ist nicht faul, daher sindfoldl'
viele der Überlegungen hier nicht zutreffend.foldl'
, da es sich um strenge Sprachen handelt, nein? Bedeutet das nicht, dass all diese Versionen Stapelüberläufe verursachen, wie dies derfoldl
Fall ist?Wie Konrad betont, ist ihre Semantik unterschiedlich. Sie haben nicht einmal den gleichen Typ:
Beispielsweise kann der Listenanhangsoperator (++) mit
foldr
as implementiert werdenwährend
gibt Ihnen einen Tippfehler.
quelle
foldl subtract 0 [1, 2, 3, 4]
ausgewertet nach-10
, währendfoldr subtract 0 [1, 2, 3, 4]
ausgewertet nach-2
.foldl
ist eigentlich0 - 1 - 2 - 3 - 4
solangefoldr
ist4 - 3 - 2 - 1 - 0
.foldr (-) 0 [1, 2, 3, 4]
ist-2
undfoldl (-) 0 [1, 2, 3, 4]
ist-10
. Auf der anderen Seitesubtract
ist rückwärts von dem, was Sie erwarten könnten (subtract 10 14
ist4
), sofoldr subtract 0 [1, 2, 3, 4]
ist-10
undfoldl subtract 0 [1, 2, 3, 4]
ist2
(positiv).