¿Alguien aquí tiene algún código útil que use la función reduce () en python? ¿Hay algún código que no sea el habitual + y * que vemos en los ejemplos?
Consulte el destino de reduce () en Python 3000 por GvR
¿Alguien aquí tiene algún código útil que use la función reduce () en python? ¿Hay algún código que no sea el habitual + y * que vemos en los ejemplos?
Consulte el destino de reduce () en Python 3000 por GvR
Respuestas:
Los otros usos que he encontrado para él además de + y * fueron con y y o, pero ahora tenemos any
y all
para reemplazar esos casos.
foldl
y foldr
aparecen mucho en Scheme ...
Aquí hay algunos usos lindos:
Acoplar una lista
Objetivo: convertirse [[1, 2, 3], [4, 5], [6, 7, 8]]
en [1, 2, 3, 4, 5, 6, 7, 8]
.
reduce(list.__add__, [[1, 2, 3], [4, 5], [6, 7, 8]], [])
Lista de dígitos a un número
Objetivo: convertirse [1, 2, 3, 4, 5, 6, 7, 8]
en 12345678
.
Feo, camino lento:
int("".join(map(str, [1,2,3,4,5,6,7,8])))
Bonita reduce
manera:
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)
toma ~ 0.09 segundos mientras 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)
toma 0.36 segundos (aproximadamente 4 veces más lento). Básicamente, la multiplicación por 10 se vuelve costosa cuando la lista se hace grande, mientras que int to str y la concatenación se mantienen baratas.
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)
toma 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)
toma 0.12 sy el conversión de dígitos al método str toma 0.16 s.
reduce()
se puede usar para encontrar el mínimo común múltiplo de 3 o más números :
#!/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)
Ejemplo:
>>> lcm(100, 23, 98)
112700
>>> lcm(*range(1, 20))
232792560
lcm
en la segunda línea?
Encuentre la intersección de N listas dadas:
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))
devoluciones:
result = set([3, 4, 5])
El uso de reduce
eso que encontré en mi código involucró la situación en la que tenía cierta estructura de clase para la expresión lógica y necesitaba convertir una lista de estos objetos de expresión en una conjunción de las expresiones. Ya tenía una función make_and
para crear una conjunción dada dos expresiones, así que escribí reduce(make_and,l)
. (Sabía que la lista no estaba vacía; de lo contrario, habría sido algo así reduce(make_and,l,make_true)
).
Esta es exactamente la razón por la que (a algunos) programadores funcionales les gusta reduce
(o pliegan funciones, como se suele llamar a tales funciones). A menudo existen ya muchas funciones binarias como +
, *
, min
, max
, concatenación y, en mi caso, make_and
y make_or
. Tener un reduce
hace que sea trivial levantar estas operaciones a listas (o árboles o lo que sea que tenga, para funciones de plegado en general).
Por supuesto, si a sum
menudo se usan ciertas instancias (como ), entonces no querrá seguir escribiendo reduce
. Sin embargo, en lugar de definirlo sum
con algún ciclo for, puede definirlo con la misma facilidad reduce
.
La legibilidad, como lo mencionaron otros, es de hecho un problema. Sin embargo, podría argumentar que la única razón por la cual las personas encuentran reduce
menos "claro" es porque no es una función que mucha gente conoce y / o usa.
and
operador: L and reduce(make_and, L)
si devolver la lista vacía es apropiado en este caso
Composición de funciones : si ya tiene una lista de funciones que le gustaría aplicar en sucesión, como:
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]
Luego puede aplicarlos todos consecutivamente con:
>>> 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'
En este caso, el encadenamiento de métodos puede ser más legible. Pero a veces no es posible, y este tipo de composición puede ser más fácil de leer y mantener que un f1(f2(f3(f4(x))))
tipo de sintaxis.
Puedes reemplazar value = json_obj['a']['b']['c']['d']['e']
con:
value = reduce(dict.__getitem__, 'abcde', json_obj)
Si ya tiene la ruta a/b/c/..
como una lista. Por ejemplo, cambie los valores en dict de dictados anidados utilizando elementos en una lista .
@Blair Conrad: También podría implementar su glob / reduce usando la suma, así:
files = sum([glob.glob(f) for f in args], [])
Esto es menos detallado que cualquiera de sus dos ejemplos, es perfectamente pitónico y sigue siendo solo una línea de código.
Entonces, para responder la pregunta original, personalmente trato de evitar el uso de reducir porque nunca es realmente necesario y encuentro que es menos claro que otros enfoques. Sin embargo, algunas personas se acostumbran a reducir y prefieren enumerar las comprensiones (especialmente los programadores de Haskell). Pero si aún no está pensando en un problema en términos de reducción, probablemente no necesite preocuparse por usarlo.
sum
y reduce
conducen a un comportamiento cuadrático. Se puede hacer en un tiempo lineal: files = chain.from_iterable(imap(iglob, args))
. Aunque probablemente no importe en este caso debido al tiempo que le toma a glob () acceder a un disco.
reduce
se puede usar para admitir búsquedas de atributos encadenados:
reduce(getattr, ('request', 'user', 'email'), self)
Por supuesto, esto es equivalente a
self.request.user.email
pero es útil cuando su código necesita aceptar una lista arbitraria de atributos.
(Los atributos encadenados de longitud arbitraria son comunes cuando se trata de modelos Django).
reduce
es útil cuando necesita encontrar la unión o intersección de una secuencia de set
objetos similares.
>>> reduce(operator.or_, ({1}, {1, 2}, {1, 3})) # union
{1, 2, 3}
>>> reduce(operator.and_, ({1}, {1, 2}, {1, 3})) # intersection
{1}
(Además de los set
s reales , un ejemplo de estos son los objetos Q de Django ).
Por otro lado, si está tratando con bool
s, debe usar any
y all
:
>>> any((True, False, True))
True
Después de ajustar mi código, parece que lo único que he usado para reducir es calcular el factorial:
reduce(operator.mul, xrange(1, x+1) or (1,))
Estoy escribiendo una función de composición para un idioma, así que construyo la función compuesta usando reducir junto con mi operador de aplicación.
En pocas palabras, componer toma una lista de funciones para componer en una sola función. Si tengo una operación compleja que se aplica por etapas, quiero poner todo junto de esta manera:
complexop = compose(stage4, stage3, stage2, stage1)
De esta manera, puedo aplicarlo a una expresión así:
complexop(expression)
Y quiero que sea equivalente a:
stage4(stage3(stage2(stage1(expression))))
Ahora, para construir mis objetos internos, quiero que diga:
Lambda([Symbol('x')], Apply(stage4, Apply(stage3, Apply(stage2, Apply(stage1, Symbol('x'))))))
(La clase Lambda crea una función definida por el usuario y Apply crea una aplicación de función).
Ahora, reducir, desafortunadamente, se dobla de la manera incorrecta, así que terminé usando, aproximadamente:
reduce(lambda x,y: Apply(y, x), reversed(args + [Symbol('x')]))
Para averiguar qué reducción produce, pruebe estos en 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)
para generar todas las posibles combinaciones de las funciones para las pruebas de rendimiento.
reducir se puede utilizar para obtener la lista con el enésimo elemento máximo
reduce(lambda x,y: x if x[2] > y[2] else y,[[1,2,3,4],[5,2,5,7],[1,6,0,2]])
devolvería [5, 2, 5, 7] ya que es la lista con un tercer elemento máximo +
Reducir no se limita a operaciones escalares; También se puede utilizar para clasificar cosas en cubos. (Esto es lo que uso reducir para la mayoría de las veces).
Imagine un caso en el que tiene una lista de objetos y desea reorganizarla jerárquicamente en función de las propiedades almacenadas de manera plana en el objeto. En el siguiente ejemplo, produzco una lista de objetos de metadatos relacionados con artículos en un periódico codificado en XML con la articles
función. articles
genera una lista de elementos XML y luego los mapea uno por uno, produciendo objetos que contienen información interesante sobre ellos. En la parte frontal, voy a querer que el usuario explore los artículos por sección / subsección / título. Así que solía reduce
tomar la lista de artículos y devolver un solo diccionario que refleja la jerarquía de sección / subsección / artículo.
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, {})
Doy ambas funciones aquí porque creo que muestra cómo map y reduce pueden complementarse muy bien cuando se trata de objetos. Lo mismo podría haberse logrado con un bucle for ... pero pasar un tiempo serio con un lenguaje funcional ha tendido a hacerme pensar en términos de mapa y reducir.
Por cierto, si alguien tiene una mejor manera de establecer propiedades como lo estoy haciendo yo extract
, donde los padres de la propiedad que desea establecer aún no existen, por favor hágamelo saber.
No estoy seguro de si esto es lo que busca, pero puede buscar el código fuente en Google .
Siga el enlace para buscar 'function: reduce () lang: python' en la búsqueda de Google Code
A primera vista, los siguientes proyectos utilizan reduce()
etc. etc., pero estos no son sorprendentes ya que son grandes proyectos.
La funcionalidad de reducir se puede hacer usando la función de recursión, que supongo que Guido pensó que era más explícita.
Actualizar:
Dado que la búsqueda de código de Google se suspendió el 15 de enero de 2012, además de volver a las búsquedas regulares de Google, hay algo llamado Colección de fragmentos de código que parece prometedor. Varios otros recursos se mencionan en las respuestas a esta pregunta (cerrada) ¿ Reemplazo para Google Code Search? .
Actualización 2 (29 de mayo de 2017):
Una buena fuente para ejemplos de Python (en código de código abierto) es el motor de búsqueda Nullege .
for
bucle.
lang:python "reduce("
encontrará definiciones que reduce
dependerán del estilo de codificación del código fuente.
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']
# )},
# [])
#},
#[])
Solía reduce
concatenar una lista de vectores de búsqueda de PostgreSQL con el ||
operador en 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)
Tengo una antigua implementación Python de pipegrep que usa reduce y el módulo glob para construir una lista de archivos para procesar:
files = []
files.extend(reduce(lambda x, y: x + y, map(glob.glob, args)))
Lo encontré útil en ese momento, pero realmente no es necesario, ya que algo similar es igual de bueno, y probablemente más legible
files = []
for f in args:
files.extend(glob.glob(f))
files = [glob.glob(f) for f in args]
itertools
, usando la flatten()
receta de docs.python.org/library/itertools.html , y luego escribiendo: files = flatten(glob.glob(f) for f in args)
(Y esta vez, probé el código antes de publicarlo, y sé que esto funciona correctamente.)
files = chain.from_iterable(imap(iglob, args))
where chain
, imap
are from itertools
module y glob.iglob
es útil si un patrón de args
puede generar archivos de varios directorios.
Digamos que hay algunos datos estadísticos anuales almacenados en una lista de contadores. Queremos encontrar los valores MIN / MAX en cada mes a través de los diferentes años. Por ejemplo, para enero sería 10. Y para febrero sería 15. Necesitamos almacenar los resultados en un nuevo contador.
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
Tengo objetos que representan algún tipo de intervalos superpuestos (exones genómicos) y redefiní su intersección usando __and__
:
class Exon:
def __init__(self):
...
def __and__(self,other):
...
length = self.length + other.length # (e.g.)
return self.__class__(...length,...)
Luego, cuando tengo una colección de ellos (por ejemplo, en el mismo gen), uso
intersection = reduce(lambda x,y: x&y, exons)
Acabo de encontrar un uso útil de reduce
: dividir cadenas sin quitar el delimitador . El código es completamente del blog Programatic Speaking. Aquí está el código:
reduce(lambda acc, elem: acc[:-1] + [acc[-1] + elem] if elem == "\n" else acc + [elem], re.split("(\n)", "a\nb\nc\n"), [])
Aquí está el resultado:
['a\n', 'b\n', 'c\n', '']
Tenga en cuenta que maneja casos extremos que la respuesta popular en SO no. Para una explicación más detallada, te estoy redirigiendo a la publicación original del blog.
Usando reduce () para averiguar si una lista de fechas es consecutiva:
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
permite que el mismo código funcione en Python 2 y 3.