Premièrement, Real World Haskell , que je lis, dit de ne jamais utiliser foldl
et d'utiliser à la place foldl'
. Alors je lui fais confiance.
Mais je suis brumeux quand utiliser foldr
contre foldl'
. Bien que je puisse voir la structure de leur fonctionnement différemment présentée devant moi, je suis trop stupide pour comprendre quand «ce qui est mieux». Je suppose qu'il me semble que peu importe ce qui est utilisé, car ils produisent tous les deux la même réponse (n'est-ce pas?). En fait, mon expérience précédente avec cette construction vient de Ruby inject
et Clojure reduce
, qui ne semblent pas avoir de versions «gauche» et «droite». (Question secondaire: quelle version utilisent-ils?)
Toute perspicacité qui peut aider une sorte intelligente comme moi serait très appréciée!
la source
fodlWhile
); 2. convertissez-le en un scan gauche (scanl
) et arrêtez-le aveclast . takeWhile p
ou similaire. Euh, et 3. utilisermapAccumL
. :)foldl
, qui collecte son résultat global et ne le produit qu'une fois que tout le travail est terminé et qu'il n'y a plus d'étapes à effectuer. Donc par exemplefoldl (flip (:)) [] [1..3] == [3,2,1]
, doncscanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... IOW,foldl f z xs = last (scanl f z xs)
et les listes infinies n'ont pas de dernier élément (qui, dans l'exemple ci-dessus, serait elle-même une liste infinie, de INF à 1).foldr
ressemble à ça:foldl
ressemble à ça:Contexte: plier sur le wiki Haskell
la source
Leur sémantique diffère donc vous ne pouvez pas simplement échanger
foldl
etfoldr
. L'un replie les éléments par la gauche, l'autre par la droite. De cette façon, l'opérateur est appliqué dans un ordre différent. Ceci est important pour toutes les opérations non associatives, telles que la soustraction.Haskell.org a un article intéressant sur le sujet.
la source
En bref,
foldr
c'est mieux lorsque la fonction d'accumulateur est paresseuse sur son deuxième argument. Pour en savoir plus, consultez Stack Overflow de Haskell wiki (jeu de mots).la source
La raison
foldl'
est préféréefoldl
pour 99% de toutes les utilisations est qu'il peut fonctionner dans un espace constant pour la plupart des utilisations.Prenez la fonction
sum = foldl['] (+) 0
. Quandfoldl'
est utilisé, la somme est immédiatement calculée, donc l'applicationsum
à une liste infinie ne fonctionnera que pour toujours, et très probablement dans un espace constant (si vous utilisez des choses commeInt
s,Double
s,Float
s.Integer
S utilisera plus qu'un espace constant si le nombre devient supérieur àmaxBound :: Int
).Avec
foldl
, un bruit sourd est construit (comme une recette sur la façon d'obtenir la réponse, qui peut être évaluée plus tard, plutôt que de stocker la réponse). Ces thunks peuvent prendre beaucoup de place, et dans ce cas, il vaut mieux évaluer l'expression que de stocker le thunk (conduisant à un débordement de pile… et vous conduisant à… oh tant pis)J'espère que cela pourra aider.
la source
foldl
ne fait rien d'autre qu'appliquer des constructeurs à un ou plusieurs de ses arguments.foldl
est réellement le meilleur choix? (Comme les listes infinies, quandfoldr
est le mauvais choix, enfoldr
étant le mauvais choix pour des listes infinies. En fait, vous ne devriez pas utiliserfoldl
oufoldl'
pour des listes infinies. Voir le wiki Haskell sur les débordements de pileÀ propos, Ruby
inject
et Clojurereduce
sontfoldl
(oufoldl1
, selon la version que vous utilisez). Habituellement, quand il n'y a qu'une seule forme dans un langage, c'est un pli gauche, y compris celui de Pythonreduce
, PerlList::Util::reduce
, C ++accumulate
, C #Aggregate
, Smalltalkinject:into:
, PHParray_reduce
, MathematicaFold
, etc. Common Lisp est parreduce
défaut le pli gauche mais il y a une option pour le pli droit.la source
reduce
n'est pas paresseux, donc c'estfoldl'
et la plupart des considérations ici ne s'appliquent pas.foldl'
, car ce sont des langues strictes, non? Sinon, cela ne signifie-t-il pas que toutes ces versions provoquent des débordements de pile comme lefoldl
fait?Comme le souligne Konrad , leur sémantique est différente. Ils n'ont même pas le même type:
Par exemple, l'opérateur d'ajout de liste (++) peut être implémenté avec
foldr
astandis que
vous donnera une erreur de type.
la source
foldl subtract 0 [1, 2, 3, 4]
évalue à-10
, tandis quefoldr subtract 0 [1, 2, 3, 4]
évalue à-2
.foldl
est en fait0 - 1 - 2 - 3 - 4
pendant quefoldr
est4 - 3 - 2 - 1 - 0
.foldr (-) 0 [1, 2, 3, 4]
est-2
etfoldl (-) 0 [1, 2, 3, 4]
est-10
. D'autre part,subtract
est à l'envers de ce à quoi vous pourriez vous attendre (subtract 10 14
est4
),foldr subtract 0 [1, 2, 3, 4]
est donc-10
etfoldl subtract 0 [1, 2, 3, 4]
est2
(positif).