Quelle est la manière la plus idiomatique de réaliser quelque chose comme ce qui suit, dans Haskell:
foldl (+) 0 [1,2,3,4,5]
--> 15
Ou son équivalent en Ruby:
[1,2,3,4,5].inject(0) {|m,x| m + x}
#> 15
De toute évidence, Python fournit la reduce
fonction, qui est une implémentation de fold, exactement comme ci-dessus, cependant, on m'a dit que la méthode de programmation `` pythonique '' était d'éviter les lambda
termes et les fonctions d'ordre supérieur, préférant les compréhensions de listes lorsque cela était possible. Par conséquent, existe-t-il un moyen préféré de plier une liste, ou une structure de type liste en Python qui n'est pas la reduce
fonction, ou est-ce reduce
la manière idiomatique d'y parvenir?
sum
n'est pas assez bon?sum
, vous voudrez peut-être fournir différents types d'exemples.sum()
fournit en fait des fonctionnalités limitées avec cela.sum([[a], [b, c, d], [e, f]], [])
renvoie[a, b, c, d, e, f]
par exemple.+
sur les listes est une opération temporelle linéaire à la fois dans le temps et dans la mémoire, ce qui rend l'appel quadratique. L'utilisationlist(itertools.chain.from_iterable([a], [b,c,d],[e,f],[]])
est globalement linéaire - et si vous n'avez besoin de l'itérer qu'une seule fois, vous pouvez abandonner l'appel àlist
pour le rendre constant en termes de mémoire.Réponses:
La manière pythonique de sommer un tableau utilise
sum
. À d'autres fins, vous pouvez parfois utiliser une combinaison dereduce
(dufunctools
module) et duoperator
module, par exemple:Sachez que
reduce
c'est en fait unfoldl
, en termes Haskell. Il n'y a pas de syntaxe spéciale pour effectuer des replis, il n'y a pas de fonction intégréefoldr
et l'utilisationreduce
avec des opérateurs non associatifs est considérée comme un mauvais style.L'utilisation de fonctions d'ordre supérieur est assez pythonique; il fait bon usage du principe de Python selon lequel tout est un objet, y compris les fonctions et les classes. Vous avez raison de dire que les lambdas sont désapprouvés par certains pythonistes, mais surtout parce qu'ils ont tendance à ne pas être très lisibles lorsqu'ils deviennent complexes.
la source
reduce()
est à peu près limitée aux opérateurs associatifs, et dans tous les autres cas, il est préférable d'écrire explicitement la boucle d'accumulation». Son utilisation est donc limitée, mais même GvR a apparemment dû l'admettre suffisamment utile pour le conserver dans la bibliothèque standard.Haskell
foldl (+) 0 [1,2,3,4,5]
Python
reduce(lambda a,b: a+b, [1,2,3,4,5], 0)
De toute évidence, c'est un exemple trivial pour illustrer un point. En Python, vous le feriez
sum([1,2,3,4,5])
et même les puristes de Haskell préféreraient généralementsum [1,2,3,4,5]
.Pour les scénarios non triviaux où il n'y a pas de fonction de commodité évidente, l'approche pythonique idiomatique consiste à écrire explicitement la boucle for et à utiliser l'affectation de variable mutable au lieu d'utiliser
reduce
ou afold
.Ce n'est pas du tout le style fonctionnel, mais c'est la manière «pythonique». Python n'est pas conçu pour les puristes fonctionnels. Découvrez comment Python favorise les exceptions pour le contrôle de flux pour voir à quel point python idiomatique est non fonctionnel.
la source
Dans Python 3, le
reduce
a été supprimé: Notes de version . Néanmoins, vous pouvez utiliser le module functoolsD'autre part, la documentation exprime une préférence pour
for
-loop au lieu dereduce
, d'où:la source
reduce
n'a pas été supprimé de la bibliothèque standard Python 3.reduce
déplacé vers lefunctools
module que vous montrez.En commençant
Python 3.8
, et en introduisant des expressions d'affectation (PEP 572) (:=
opérateur), qui donne la possibilité de nommer le résultat d'une expression, nous pouvons utiliser une compréhension de liste pour répliquer ce que les autres langages appellent les opérations fold / foldleft / reduction:Étant donné une liste, une fonction réductrice et un accumulateur:
on peut se plier
items
avecf
pour obtenir le résultataccumulation
:ou sous forme condensée:
Notez qu'il s'agit en fait également d'une opération "scanleft" car le résultat de la compréhension de liste représente l'état de l'accumulation à chaque étape:
la source
Vous pouvez également réinventer la roue:
la source
f
dans votre cas récursif.reduce
qui offre déjà (notez que la signature de la fonction de réduction estreduce(function, sequence[, initial]) -> value
- elle inclut également la fonctionnalité de donner une valeur initiale accumulateur).Pas vraiment de réponse à la question, mais des one-liners pour foldl et foldr:
la source
reduce(lambda y, x: x**y, reversed(a))
. Il a maintenant une utilisation plus naturelle, fonctionne avec des itérateurs et consomme moins de mémoire.La vraie réponse à ce problème (de réduire) est: Utilisez simplement une boucle!
Ce sera plus rapide qu'une réduction et des choses comme PyPy peuvent optimiser des boucles comme ça.
BTW, le cas de somme doit être résolu avec la
sum
fonctionla source
reduce
est un moyen courant d'optimiser un programme Python.product
contre un dans votre style, et c'est plus rapide (marginalement, cependant).operator.add
) comme argument à réduire: cet appel supplémentaire est un appel C (qui est beaucoup moins cher qu'un appel Python), et cela évite de distribuer et d'interpréter quelques instructions de bytecode, ce qui peut facilement provoquer des dizaines de appels de fonction.Je crois que certains des répondants à cette question n'ont pas compris l'implication plus large de la
fold
fonction en tant qu'outil abstrait. Oui,sum
on peut faire la même chose pour une liste d'entiers, mais c'est un cas trivial.fold
est plus générique. Il est utile lorsque vous avez une séquence de structures de données de forme variable et que vous souhaitez exprimer proprement une agrégation. Ainsi, au lieu d'avoir à construire unefor
boucle avec une variable d'agrégation et à la recalculer manuellement à chaque fois, unefold
fonction (ou la version Python, quireduce
semble correspondre à) permet au programmeur d'exprimer beaucoup plus clairement l'intention de l'agrégation en fournissant simplement deux choses:la source
fold
qui est difficile à faire proprement en Python, puis "fold
" cela en Python :-)Je suis peut-être assez en retard à la fête, mais nous pouvons créer une personnalisation en
foldr
utilisant un simple calcul lambda et une fonction curry. Voici mon implémentation de foldr en python.Même si l'implémentation est récursive (peut être lente), elle affichera les valeurs
15
et120
respectivementla source