Em primeiro lugar, o Mundo Real Haskell , que estou lendo, diz para nunca usar foldl
e usar foldl'
. Então eu confio nisso.
Mas eu sou vago sobre quando usar foldr
vs. foldl'
. Embora eu possa ver a estrutura de como eles funcionam de maneira diferente na minha frente, sou burra demais para entender quando "o que é melhor". Acho que me parece que realmente não deveria importar o que é usado, pois ambos produzem a mesma resposta (não é?). De fato, minha experiência anterior com esse construto é de Ruby inject
e Clojure reduce
, que não parecem ter versões "esquerda" e "direita". (Pergunta secundária: qual versão eles usam?)
Qualquer insight que possa ajudar um tipo de smarts desafiado como eu seria muito apreciado!
fonte
fodlWhile
); 2. converta-o em uma varredura esquerda (scanl
) e interrompa-a comlast . takeWhile p
ou similar. Uh, e 3. usemapAccumL
. :)foldl
, que coleta o resultado geral e o produz somente depois que todo o trabalho é concluído e não há mais etapas a serem executadas. Entãofoldl (flip (:)) [] [1..3] == [3,2,1]
, por exemplo , entãoscanl (flip(:)) [] [1..] = [[],[1],[2,1],[3,2,1],...]
... IOWfoldl f z xs = last (scanl f z xs)
e listas infinitas não têm último elemento (que, no exemplo acima, seria uma lista infinita, de INF a 1).foldr
se parece com isso:foldl
se parece com isso:Contexto: dobre no wiki Haskell
fonte
Sua semântica é diferente, então você não pode simplesmente trocar
foldl
efoldr
. Um dobra os elementos da esquerda e o outro da direita. Dessa forma, o operador é aplicado em uma ordem diferente. Isso é importante para todas as operações não associativas, como subtração.Haskell.org tem um artigo interessante sobre o assunto.
fonte
Em breve,
foldr
é melhor quando a função acumulador é preguiçosa em seu segundo argumento. Leia mais em Stack Overflow do wiki da Haskell (trocadilhos).fonte
O motivo
foldl'
preferidofoldl
para 99% de todos os usos é que ele pode ser executado em espaço constante para a maioria dos usos.Tome a função
sum = foldl['] (+) 0
. Quandofoldl'
é usada, a soma é calculada imediatamente; portanto, a aplicaçãosum
a uma lista infinita será executada para sempre e provavelmente no espaço constante (se você estiver usando coisas comoInt
s,Double
s,Float
s.Integer
S usará mais do que o espaço constante se o número fica maior quemaxBound :: Int
).Com
foldl
, um thunk é construído (como uma receita de como obter a resposta, que pode ser avaliada mais tarde, em vez de armazenar a resposta). Esses thunks podem ocupar muito espaço e, nesse caso, é muito melhor avaliar a expressão do que armazenar o thunk (causando um estouro de pilha… e levando você a… oh, não importa)Espero que ajude.
fonte
foldl
não fizer nada além de aplicar construtores a um ou mais de seus argumentos.foldl
é realmente a melhor escolha? (Como listas infinitas quandofoldr
é a escolha errada, otimização-wise.?)foldr
a escolha errada para listas infinitas. De fato, você não deve usarfoldl
oufoldl'
para listas infinitas. Veja o wikiA propósito, Ruby
inject
e Clojurereduce
sãofoldl
(oufoldl1
, dependendo da versão que você usa). Normalmente, quando existe apenas um formulário em uma linguagem, é uma dobra à esquerda, incluindo Pythonreduce
, PerlList::Util::reduce
, C ++accumulate
, C #Aggregate
, Smalltalkinject:into:
, PHParray_reduce
, MathematicaFold
, etc. Oreduce
padrão do Common Lisp é o padrão para a esquerda, mas há uma opção para a direita.fonte
reduce
não é preguiçoso, portanto,foldl'
e muitas das considerações aqui não se aplicam.foldl'
, como são línguas estritas, não? Caso contrário, isso não significa que todas essas versões causam estouros de pilha, comofoldl
faz?Como Konrad aponta, sua semântica é diferente. Eles nem têm o mesmo tipo:
Por exemplo, o operador de adição de lista (++) pode ser implementado com
foldr
o seguinteenquanto
lhe dará um erro de tipo.
fonte
foldl subtract 0 [1, 2, 3, 4]
avalia para-10
, enquantofoldr subtract 0 [1, 2, 3, 4]
avalia para-2
.foldl
é realmente0 - 1 - 2 - 3 - 4
enquantofoldr
é4 - 3 - 2 - 1 - 0
.foldr (-) 0 [1, 2, 3, 4]
é-2
efoldl (-) 0 [1, 2, 3, 4]
é-10
. Por outro lado,subtract
está ao contrário do que você poderia esperar (subtract 10 14
é4
), assimfoldr subtract 0 [1, 2, 3, 4]
é-10
efoldl subtract 0 [1, 2, 3, 4]
é2
(positivo).