Quelqu'un ici a-t-il un code utile qui utilise la fonction reduction () en python? Existe-t-il un code autre que les + et * habituels que nous voyons dans les exemples?
Reportez-vous à Fate of Reduce () en Python 3000 par GvR
Quelqu'un ici a-t-il un code utile qui utilise la fonction reduction () en python? Existe-t-il un code autre que les + et * habituels que nous voyons dans les exemples?
Reportez-vous à Fate of Reduce () en Python 3000 par GvR
Réponses:
Les autres utilisations que j'ai trouvées pour cela en plus de + et * étaient avec et et ou, mais maintenant nous avons any
et all
pour remplacer ces cas.
foldl
et foldr
viennent souvent dans Scheme ...
Voici quelques usages mignons:
Aplatir une liste
Objectif: [[1, 2, 3], [4, 5], [6, 7, 8]]
devenir [1, 2, 3, 4, 5, 6, 7, 8]
.
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Liste des chiffres d'un nombre
Objectif: [1, 2, 3, 4, 5, 6, 7, 8]
devenir 12345678
.
Voie moche et lente:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
Jolie reduce
façon:
reduce(lambda a,d: 10*a+d, [1,2,3,4,5,6,7,8], 0)
timeit.repeat('int("".join(map(str, digit_list)))', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)
prend environ 0,09 seconde tandis que timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,1000))', number=1000)
prend 0,36 seconde (environ 4 fois plus lent). Fondamentalement, la multiplication par 10 devient coûteuse lorsque la liste devient grande, tandis que int vers str et la concaténation restent bon marché.
timeit.repeat('convert_digit_list_to_int(digit_list)', setup = 'digit_list = [d%10 for d in xrange(1,10)]\ndef convert_digit_list_to_int(digits):\n i = 0\n for d in digits:\n i = 10*i + d\n return i', number=100000)
prend 0,06 s, timeit.repeat('reduce(lambda a,d: 10*a+d, digit_list)', setup = 'digit_list = list(d%10 for d in xrange(1,10))', number=100000)
prend 0,12 s et la conversion des chiffres en méthode str prend 0,16 s.
reduce()
peut être utilisé pour trouver le plus petit multiple commun pour 3 nombres ou plus :
#!/usr/bin/env python
from fractions import gcd
from functools import reduce
def lcm(*args):
return reduce(lambda a,b: a * b // gcd(a, b), args)
Exemple:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
lcm
dans la deuxième ligne?
Trouvez l'intersection de N listes données:
input_list = [[1, 2, 3, 4, 5], [2, 3, 4, 5, 6], [3, 4, 5, 6, 7]]
result = reduce(set.intersection, map(set, input_list))
Retour:
result = set([3, 4, 5])
L'utilisation de ce reduce
que j'ai trouvé dans mon code impliquait la situation où j'avais une structure de classe pour l'expression logique et j'avais besoin de convertir une liste de ces objets d'expression en une conjonction des expressions. J'avais déjà une fonction make_and
pour créer une conjonction à partir de deux expressions, alors j'ai écrit reduce(make_and,l)
. (Je savais que la liste n'était pas vide; sinon, cela aurait été quelque chose comme ça reduce(make_and,l,make_true)
.)
C'est exactement la raison pour laquelle (certains) programmeurs fonctionnels aiment reduce
(ou replient les fonctions, comme de telles fonctions, sont généralement appelées). Il y a souvent déjà de nombreuses fonctions binaires comme +
, *
, min
, max
, concaténation et, dans mon cas, make_and
et make_or
. Avoir un reduce
rend trivial de lever ces opérations vers des listes (ou des arbres ou tout ce que vous avez, pour les fonctions de repli en général).
Bien sûr, si certaines instanciations (telles que sum
) sont souvent utilisées, vous ne voulez pas continuer à écrire reduce
. Cependant, au lieu de définir le sum
avec une boucle for, vous pouvez tout aussi facilement le définir avec reduce
.
La lisibilité, comme mentionné par d'autres, est en effet un problème. Vous pourriez cependant faire valoir que la seule raison pour laquelle les gens trouvent reduce
moins «clair» est que ce n'est pas une fonction que beaucoup de gens connaissent et / ou utilisent.
and
opérateur: L and reduce(make_and, L)
si le retour d'une liste vide est approprié dans ce cas
Composition des fonctions : si vous disposez déjà d'une liste de fonctions que vous souhaitez appliquer successivement, telles que:
color = lambda x: x.replace('brown', 'blue')
speed = lambda x: x.replace('quick', 'slow')
work = lambda x: x.replace('lazy', 'industrious')
fs = [str.lower, color, speed, work, str.title]
Ensuite, vous pouvez tous les appliquer consécutivement avec:
>>> call = lambda s, func: func(s)
>>> s = "The Quick Brown Fox Jumps Over the Lazy Dog"
>>> reduce(call, fs, s)
'The Slow Blue Fox Jumps Over The Industrious Dog'
Dans ce cas, le chaînage de méthodes peut être plus lisible. Mais parfois, ce n'est pas possible, et ce type de composition peut être plus lisible et maintenable qu'une f1(f2(f3(f4(x))))
sorte de syntaxe.
Vous pouvez remplacer value = json_obj['a']['b']['c']['d']['e']
par:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Si vous avez déjà le chemin a/b/c/..
sous forme de liste. Par exemple, modifiez les valeurs dans le dictionnaire des dictionnaires imbriqués à l'aide des éléments d'une liste .
@Blair Conrad: Vous pouvez également implémenter votre glob / reduction en utilisant sum, comme ceci:
files = sum([glob.glob(f) for f in args], [])
C'est moins verbeux que l'un ou l'autre de vos deux exemples, est parfaitement pythonique et ne représente toujours qu'une seule ligne de code.
Donc, pour répondre à la question initiale, j'essaie personnellement d'éviter d'utiliser réduire car ce n'est jamais vraiment nécessaire et je trouve que c'est moins clair que d'autres approches. Cependant, certaines personnes s'habituent à réduire et en viennent à le préférer à la liste des compréhensions (en particulier les programmeurs Haskell). Mais si vous ne pensez pas déjà à un problème en termes de réduction, vous n'avez probablement pas à vous soucier de son utilisation.
sum
et reduce
conduisent à un comportement quadratique. Il peut se faire dans le temps linéaire: files = chain.from_iterable(imap(iglob, args))
. Bien que cela n'ait probablement pas d'importance dans ce cas en raison du temps nécessaire à glob () pour accéder à un disque.
reduce
peut être utilisé pour prendre en charge les recherches d'attributs chaînés:
reduce(getattr, ('request', 'user', 'email'), self)
Bien sûr, cela équivaut à
self.request.user.email
mais c'est utile lorsque votre code doit accepter une liste arbitraire d'attributs.
(Les attributs chaînés de longueur arbitraire sont courants lorsqu'il s'agit de modèles Django.)
reduce
est utile lorsque vous avez besoin de trouver l'union ou l'intersection d'une séquence d' set
objets semblables.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Outre les set
s réels , un exemple de ceux-ci sont les objets Q de Django .)
D'un autre côté, si vous traitez avec bool
s, vous devez utiliser any
et all
:
>>> any((True, False, True))
True
Après avoir greffé mon code, il semble que la seule chose que j'ai utilisée pour réduire soit le calcul du factoriel:
reduce(operator.mul, xrange(1, x+1) or (1,))
J'écris une fonction de composition pour un langage, donc je construis la fonction composée en utilisant réduire avec mon opérateur apply.
En un mot, compose prend une liste de fonctions à composer en une seule fonction. Si j'ai une opération complexe qui est appliquée par étapes, je veux tout mettre ensemble comme ceci:
complexop = compose(stage4, stage3, stage2, stage1)
De cette façon, je peux ensuite l'appliquer à une expression comme ceci:
complexop(expression)
Et je veux que ce soit équivalent à:
stage4(stage3(stage2(stage1(expression))))
Maintenant, pour construire mes objets internes, je veux qu'il dise:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(La classe Lambda crée une fonction définie par l'utilisateur et Apply crée une application de fonction.)
Maintenant, réduisez, malheureusement, les plis dans le mauvais sens, alors j'ai fini par utiliser, à peu près:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Pour comprendre ce que produit la réduction, essayez-les dans le REPL:
reduce(lambda x, y: (x, y), range(1, 11))
reduce(lambda x, y: (y, x), reversed(range(1, 11)))
compose = lambda *func: lambda arg: reduce(lambda x, f: f(x), reversed(funcs), arg)
de générer toutes les combinaisons possibles de fonctions pour les tests de performances.
réduire peut être utilisé pour obtenir la liste avec le nième élément maximum
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
renverrait [5, 2, 5, 7] car c'est la liste avec un maximum de 3ème élément +
Réduire ne se limite pas aux opérations scalaires; il peut également être utilisé pour trier les choses dans des seaux. (C'est ce que j'utilise le plus souvent pour réduire).
Imaginez un cas dans lequel vous avez une liste d'objets et que vous souhaitez la réorganiser hiérarchiquement en fonction des propriétés stockées à plat dans l'objet. Dans l'exemple suivant, je produis une liste d'objets de métadonnées liés à des articles dans un journal encodé en XML avec la articles
fonction. articles
génère une liste d'éléments XML, puis les mappe un par un, produisant des objets contenant des informations intéressantes à leur sujet. Sur le front-end, je vais vouloir laisser l'utilisateur parcourir les articles par section / sous-section / titre. J'utilise donc reduce
pour prendre la liste des articles et retourner un dictionnaire unique qui reflète la hiérarchie section / sous-section / article.
from lxml import etree
from Reader import Reader
class IssueReader(Reader):
def articles(self):
arts = self.q('//div3') # inherited ... runs an xpath query against the issue
subsection = etree.XPath('./ancestor::div2/@type')
section = etree.XPath('./ancestor::div1/@type')
header_text = etree.XPath('./head//text()')
return map(lambda art: {
'text_id': self.id,
'path': self.getpath(art)[0],
'subsection': (subsection(art)[0] or '[none]'),
'section': (section(art)[0] or '[none]'),
'headline': (''.join(header_text(art)) or '[none]')
}, arts)
def by_section(self):
arts = self.articles()
def extract(acc, art): # acc for accumulator
section = acc.get(art['section'], False)
if section:
subsection = acc.get(art['subsection'], False)
if subsection:
subsection.append(art)
else:
section[art['subsection']] = [art]
else:
acc[art['section']] = {art['subsection']: [art]}
return acc
return reduce(extract, arts, {})
Je donne les deux fonctions ici parce que je pense que cela montre comment la carte et la réduction peuvent bien se compléter lorsqu'il s'agit d'objets. La même chose aurait pu être accomplie avec une boucle for, ... mais passer du temps sérieux avec un langage fonctionnel a eu tendance à me faire penser en termes de map et de réduire.
Au fait, si quelqu'un a un meilleur moyen de définir des propriétés comme je le fais dans extract
, où les parents de la propriété que vous souhaitez définir n'existent peut-être pas encore, veuillez me le faire savoir.
Je ne sais pas si c'est ce que vous recherchez, mais vous pouvez rechercher le code source sur Google .
Suivez le lien pour une recherche sur 'function: reduction () lang: python' sur la recherche Google Code
À première vue, les projets suivants utilisent reduce()
etc. etc. mais alors ce ne sont guère surprenants car ce sont d'énormes projets.
La fonctionnalité de réduction peut être réalisée en utilisant la récursivité de la fonction que Guido pensait plus explicite.
Mettre à jour:
Depuis que la recherche de code de Google a été interrompue le 15 janvier 2012, en plus du retour aux recherches Google régulières, il y a quelque chose appelé Collection d'extraits de code qui semble prometteur. Un certain nombre d'autres ressources sont mentionnées dans les réponses à cette question (fermée) Remplacement de Google Code Search? .
Mise à jour 2 (29 mai 2017):
Une bonne source d'exemples Python (en code open-source) est le moteur de recherche Nullege .
for
boucle.
lang:python "reduce("
trouvera des définitions en reduce
fonction du style de codage du code source.
import os
files = [
# full filenames
"var/log/apache/errors.log",
"home/kane/images/avatars/crusader.png",
"home/jane/documents/diary.txt",
"home/kane/images/selfie.jpg",
"var/log/abc.txt",
"home/kane/.vimrc",
"home/kane/images/avatars/paladin.png",
]
# unfolding of plain filiname list to file-tree
fs_tree = ({}, # dict of folders
[]) # list of files
for full_name in files:
path, fn = os.path.split(full_name)
reduce(
# this fucction walks deep into path
# and creates placeholders for subfolders
lambda d, k: d[0].setdefault(k, # walk deep
({}, [])), # or create subfolder storage
path.split(os.path.sep),
fs_tree
)[1].append(fn)
print fs_tree
#({'home': (
# {'jane': (
# {'documents': (
# {},
# ['diary.txt']
# )},
# []
# ),
# 'kane': (
# {'images': (
# {'avatars': (
# {},
# ['crusader.png',
# 'paladin.png']
# )},
# ['selfie.jpg']
# )},
# ['.vimrc']
# )},
# []
# ),
# 'var': (
# {'log': (
# {'apache': (
# {},
# ['errors.log']
# )},
# ['abc.txt']
# )},
# [])
#},
#[])
J'avais l'habitude reduce
de concaténer une liste de vecteurs de recherche PostgreSQL avec l' ||
opérateur dans sqlalchemy-searchable:
vectors = (self.column_vector(getattr(self.table.c, column_name))
for column_name in self.indexed_columns)
concatenated = reduce(lambda x, y: x.op('||')(y), vectors)
compiled = concatenated.compile(self.conn)
J'ai une ancienne implémentation Python de pipegrep qui utilise réduire et le module glob pour créer une liste de fichiers à traiter:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
Je l'ai trouvé pratique à l'époque, mais ce n'est vraiment pas nécessaire, car quelque chose de similaire est tout aussi bon, et probablement plus lisible
files = []
for f in args:
files.extend(glob.glob(f))
files = [glob.glob(f) for f in args]
itertools
, en utilisant la flatten()
recette de docs.python.org/library/itertools.html , puis d'écrire: files = flatten(glob.glob(f) for f in args)
(Et cette fois, j'ai testé le code avant de le publier, et je sais que cela fonctionne correctement.)
files = chain.from_iterable(imap(iglob, args))
où chain
, imap
proviennent du itertools
module et glob.iglob
est utile si un modèle de args
peut générer des fichiers de plusieurs répertoires.
Supposons qu'il existe des données statistiques annuelles stockées dans une liste de compteurs. Nous voulons trouver les valeurs MIN / MAX de chaque mois sur les différentes années. Par exemple, pour janvier, ce serait 10. Et pour février, ce serait 15. Nous devons stocker les résultats dans un nouveau compteur.
from collections import Counter
stat2011 = Counter({"January": 12, "February": 20, "March": 50, "April": 70, "May": 15,
"June": 35, "July": 30, "August": 15, "September": 20, "October": 60,
"November": 13, "December": 50})
stat2012 = Counter({"January": 36, "February": 15, "March": 50, "April": 10, "May": 90,
"June": 25, "July": 35, "August": 15, "September": 20, "October": 30,
"November": 10, "December": 25})
stat2013 = Counter({"January": 10, "February": 60, "March": 90, "April": 10, "May": 80,
"June": 50, "July": 30, "August": 15, "September": 20, "October": 75,
"November": 60, "December": 15})
stat_list = [stat2011, stat2012, stat2013]
print reduce(lambda x, y: x & y, stat_list) # MIN
print reduce(lambda x, y: x | y, stat_list) # MAX
J'ai des objets représentant une sorte d'intervalles qui se chevauchent (exons génomiques), et j'ai redéfini leur intersection en utilisant __and__
:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Ensuite, quand j'en ai une collection (par exemple, dans le même gène), j'utilise
intersection = reduce(lambda x,y: x&y, exons)
Je viens de trouver une utilisation utile de reduce
: fractionner une chaîne sans supprimer le délimiteur . Le code provient entièrement du blog Programately Speaking. Voici le code:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Voici le résultat:
['a\n', 'b\n', 'c\n', '']
Notez qu'il gère les cas extrêmes que la réponse populaire dans SO ne fait pas. Pour une explication plus approfondie, je vous redirige vers l'article de blog original.
Utilisation de réduire () pour savoir si une liste de dates est consécutive:
from datetime import date, timedelta
def checked(d1, d2):
"""
We assume the date list is sorted.
If d2 & d1 are different by 1, everything up to d2 is consecutive, so d2
can advance to the next reduction.
If d2 & d1 are not different by 1, returning d1 - 1 for the next reduction
will guarantee the result produced by reduce() to be something other than
the last date in the sorted date list.
Definition 1: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider consecutive
Definition 2: 1/1/14, 1/2/14, 1/2/14, 1/3/14 is consider not consecutive
"""
#if (d2 - d1).days == 1 or (d2 - d1).days == 0: # for Definition 1
if (d2 - d1).days == 1: # for Definition 2
return d2
else:
return d1 + timedelta(days=-1)
# datelist = [date(2014, 1, 1), date(2014, 1, 3),
# date(2013, 12, 31), date(2013, 12, 30)]
# datelist = [date(2014, 2, 19), date(2014, 2, 19), date(2014, 2, 20),
# date(2014, 2, 21), date(2014, 2, 22)]
datelist = [date(2014, 2, 19), date(2014, 2, 21),
date(2014, 2, 22), date(2014, 2, 20)]
datelist.sort()
if datelist[-1] == reduce(checked, datelist):
print "dates are consecutive"
else:
print "dates are not consecutive"
from functools import reduce
permet au même code de fonctionner sur Python 2 et 3.