Qual é a maneira mais idiomática de conseguir algo como o seguinte, em Haskell:
foldl (+) 0 [1,2,3,4,5]
--> 15
Ou seu equivalente em Ruby:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
Obviamente, Python fornece a reduce
função, que é uma implementação de fold, exatamente como acima, no entanto, fui informado que a maneira 'pítônica' de programação era evitar lambda
termos e funções de ordem superior, preferindo compreensões de lista quando possível. Portanto, há uma maneira preferida de dobrar uma lista, ou estrutura semelhante a uma lista em Python que não seja a reduce
função, ou é reduce
a maneira idiomática de fazer isso?
sum
não é bom o suficiente?sum
, você pode fornecer alguns tipos diferentes de exemplos.sum()
na verdade, fornece funcionalidade limitada com isso.sum([[a], [b, c, d], [e, f]], [])
retorna[a, b, c, d, e, f]
por exemplo.+
em listas é uma operação de tempo linear tanto no tempo quanto na memória, tornando toda a chamada quadrática. O usolist(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])
é linear no geral - e se você só precisar iterar sobre ele uma vez, pode cancelar a chamada paralist
para torná-lo constante em termos de memória.Respostas:
A maneira pitônica de somar uma matriz está usando
sum
. Para outros fins, às vezes você pode usar alguma combinação dereduce
(dofunctools
módulo) e ooperator
módulo, por exemplo:Esteja ciente de que
reduce
é realmente umfoldl
, em termos de Haskell. Não há sintaxe especial para realizar dobras, não há builtinfoldr
e, na verdade, usarreduce
com operadores não associativos é considerado um estilo incorreto.Usar funções de ordem superior é bastante pitônico; faz bom uso do princípio do Python de que tudo é um objeto, incluindo funções e classes. Você está certo ao dizer que lambdas são desaprovados por alguns Pythonistas, mas principalmente porque eles tendem a não ser muito legíveis quando se tornam complexos.
fonte
reduce()
é praticamente limitada a operadores associativos e, em todos os outros casos, é melhor escrever o loop de acumulação explicitamente." Portanto, seu uso é limitado, mas mesmo GvR aparentemente teve que admitir que é útil o suficiente para mantê-lo na biblioteca padrão.Haskell
foldl (+) 0 [1,2,3,4,5]
Pitão
reduce(lambda a,b: a+b, [1,2,3,4,5], 0)
Obviamente, esse é um exemplo trivial para ilustrar um ponto. Em Python, você apenas faria
sum([1,2,3,4,5])
e até mesmo os puristas de Haskell geralmente prefeririamsum [1,2,3,4,5]
.Para cenários não triviais quando não há função de conveniência óbvia, a abordagem idiomática pythônica é escrever explicitamente o loop for e usar a atribuição de variável mutável em vez de usar
reduce
ou afold
.Esse não é o estilo funcional, mas é a forma "pítônica". Python não foi projetado para puristas funcionais. Veja como o Python favorece as exceções para controle de fluxo para ver como o python idiomático não é funcional.
fonte
No Python 3, o
reduce
foi removido: Notas de lançamento . No entanto, você pode usar o módulo functoolsPor outro lado, a documentação expressa preferência por
for
-loop em vez dereduce
, portanto:fonte
reduce
não foi removido da biblioteca padrão do Python 3.reduce
movido para ofunctools
módulo conforme você mostra.A partir
Python 3.8
, e a introdução de expressões de atribuição (PEP 572) (:=
operador), que dá a possibilidade de nomear o resultado de uma expressão, podemos usar uma compreensão de lista para replicar o que outras línguas chamam de operações dobrar / dobrar / reduzir:Dada uma lista, uma função redutora e um acumulador:
podemos dobrar
items
com af
fim de obter o resultadoaccumulation
:ou em forma condensada:
Observe que, na verdade, essa também é uma operação "scanleft", pois o resultado da compreensão da lista representa o estado da acumulação em cada etapa:
fonte
Você também pode reinventar a roda:
fonte
f
redor no seu caso recursivo.reduce
já oferece (observe que a assinatura da função de redução éreduce(function, sequence[, initial]) -> value
- também inclui a funcionalidade de dar um valor inicial para o acumulador).Não é realmente uma resposta à pergunta, mas uma linha para foldl e foldr:
fonte
reduce(lambda y, x: x**y, reversed(a))
. Agora ele tem um uso mais natural, funciona com iteradores e consome menos memória.A resposta real para este problema (de redução) é: Basta usar um loop!
Isso será mais rápido do que reduzir e coisas como PyPy podem otimizar loops como esse.
BTW, o caso da soma deve ser resolvido com a
sum
funçãofonte
reduce
é uma maneira comum de otimizar um programa Python.product
um em seu estilo, e é mais rápido (embora marginalmente).operator.add
) como argumento para reduzir: Essa chamada extra é uma chamada C (que é muito mais barata do que uma chamada Python) e economiza o despacho e a interpretação de algumas instruções de bytecode, que podem facilmente causar dezenas de chamadas de função.Eu acredito que alguns dos respondentes desta questão perderam a implicação mais ampla da
fold
função como uma ferramenta abstrata. Sim,sum
pode fazer a mesma coisa com uma lista de inteiros, mas este é um caso trivial.fold
é mais genérico. É útil quando você tem uma sequência de estruturas de dados de formas variadas e deseja expressar claramente uma agregação. Então, em vez de ter que construir umfor
loop com uma variável agregada e recalcular manualmente a cada vez, umafold
função (ou a versão Python, quereduce
parece corresponder a) permite que o programador expresse a intenção da agregação muito mais claramente, simplesmente fornecendo duas coisas:fonte
fold
desse um exemplo não trivial de que é difícil fazer de forma limpa em Python, e então "fold
" isso em Python :-)Posso estar um pouco atrasado para a festa, mas podemos criar um costume
foldr
usando cálculo lambda simples e função curry. Aqui está minha implementação de foldr em python.Mesmo que a implementação seja recursiva (pode ser lenta), ela irá imprimir os valores
15
e120
respectivamentefonte