En primer lugar, el mundo real Haskell , que estoy leyendo, dice que nunca usefoldl
y en su lugar use foldl'
. Entonces confío en eso.
Pero estoy nebuloso sobre cuándo utilizar foldr
vs foldl'
. Aunque puedo ver la estructura de cómo funcionan de manera diferente frente a mí, soy demasiado estúpido para entender cuándo "cuál es mejor". Supongo que me parece que realmente no debería importar cuál se usa, ya que ambos producen la misma respuesta (¿no?). De hecho, mi experiencia previa con esta construcción es de Ruby inject
y Clojure reduce
, que no parecen tener versiones "izquierda" y "derecha". (Pregunta secundaria: ¿qué versión usan?)
¡Cualquier idea que pueda ayudar a un tipo con problemas de inteligencia como yo sería muy apreciada!
fuente
fodlWhile
); 2. conviértalo en un escaneo izquierdo (scanl
) y deténgalo conlast . takeWhile p
o similar. Uh, y 3. usomapAccumL
. :)foldl
que recoge su resultado general y lo produce solo después de que todo el trabajo haya terminado y no haya más pasos para realizar. Entoncesfoldl (flip (:)) [] [1..3] == [3,2,1]
, por ejemplo , entoncesscanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... IOW,foldl f z xs = last (scanl f z xs)
y las listas infinitas no tienen un último elemento (que, en el ejemplo anterior, sería una lista infinita, desde INF hasta 1).foldr
Se ve como esto:foldl
Se ve como esto:Contexto: Doblar en la wiki de Haskell
fuente
Su semántica difiere, por lo que no puedes simplemente intercambiar
foldl
yfoldr
. El primero dobla los elementos desde la izquierda, el otro desde la derecha. De esa manera, el operador se aplica en un orden diferente. Esto es importante para todas las operaciones no asociativas, como la resta.Haskell.org tiene un artículo interesante sobre el tema.
fuente
En breve,
foldr
es mejor cuando la función del acumulador es perezosa en su segundo argumento. Lea más en Stack Overflow de Haskell wiki (juego de palabras).fuente
La razón
foldl'
preferidafoldl
para el 99% de todos los usos es que puede ejecutarse en espacio constante para la mayoría de los usos.Toma la función
sum = foldl['] (+) 0
. Cuandofoldl'
se usa, la suma se calcula inmediatamente, por lo que la aplicaciónsum
a una lista infinita se ejecutará para siempre, y muy probablemente en un espacio constante (si está usando cosas comoInt
s,Double
s,Float
s.Integer
S usará más que un espacio constante si el el número se hace mayor quemaxBound :: Int
).Con
foldl
, se crea un thunk (como una receta de cómo obtener la respuesta, que puede evaluarse más tarde, en lugar de almacenar la respuesta). Estos thunks pueden ocupar mucho espacio, y en este caso, es mucho mejor evaluar la expresión que almacenar el thunk (lo que lleva a un desbordamiento de pila ... y te lleva a ... oh, no importa)Espero que ayude.
fuente
foldl
no hace más que aplicar constructores a uno o más de sus argumentos.foldl
es realmente la mejor opción? (Al igual que las listas infinitas cuandofoldr
es la elección incorrecta, en cuanto a optimización.)foldr
ser la elección incorrecta para listas infinitas. De hecho, no debe usarfoldl
ofoldl'
para listas infinitas. Vea el wiki de Haskell sobre desbordamientos de pilaPor cierto, los de Ruby
inject
y Clojurereduce
sonfoldl
(ofoldl1
, dependiendo de la versión que uses). Por lo general, cuando sólo hay un formulario en un idioma, es una izquierda pliegue, incluyendo Pythonreduce
, PerlList::Util::reduce
, C ++ 'saccumulate
, C #' sAggregate
, Smalltalkinject:into:
, PHParray_reduce
, MathematicaFold
, etc. de Common Lispreduce
por defecto a la izquierda veces, pero hay una opción para el derecho pliegue.fuente
reduce
no es vago, por lo quefoldl'
muchas de las consideraciones aquí no se aplican.foldl'
, ya que son idiomas estrictos, ¿no? De lo contrario, ¿eso no significará que todas esas versiones causan desbordamientos de pila como lofoldl
hace?Como señala Konrad , su semántica es diferente. Ni siquiera tienen el mismo tipo:
Por ejemplo, el operador de lista append (++) puede ser implementado con
foldr
comomientras
le dará un error de tipo.
fuente
foldl subtract 0 [1, 2, 3, 4]
evalúa a-10
, mientras quefoldr subtract 0 [1, 2, 3, 4]
evalúa a-2
.foldl
es en realidad0 - 1 - 2 - 3 - 4
mientrasfoldr
es4 - 3 - 2 - 1 - 0
.foldr (-) 0 [1, 2, 3, 4]
es-2
yfoldl (-) 0 [1, 2, 3, 4]
es-10
. Por otro lado,subtract
está al revés de lo que podría esperar (subtract 10 14
es4
), así quefoldr subtract 0 [1, 2, 3, 4]
es-10
yfoldl subtract 0 [1, 2, 3, 4]
es2
(positivo).