debug_mode=ON

Buscar en

 
 

Map y Reduce en Python

Escrito por anhelido hace 1 años bajo una licencia de Creative Commons Creative Commons License
2154 visitas. Etiquetas: python, map, reduce

Curioseando por la página Project Euler, he encontrado el problema nº 20 gracias a jAB. Dice lo siguiente:

Encuentra la suma de los dígitos del número 100! (Factorial de 100)

Así que me he dispuesto a hacer en Python dicho problema. La primera solución ha sido con el siguiente código fuente:

La función factorial evidente es:

def factorial(n):
    """Devuelve el factorial del argumento recibido"""
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)

Una vez tenemos la manera de calcular un factorial, procedemos con la suma del mismo:

def sumadig(n):
    """Devuelve la suma de los dígitos del argumento recibido"""

    nstr = str(n)
    lista = []

    for i in range(len(nstr)):
        lista.append(nstr[i])

    suma = 0

    for i in lista:
        suma += int(i)

    return suma

Se invoca la llamada a dicha función así (en el main):

def main():
    print sumadig(factorial(100))

if __name__ == '__main__':
    main()

Todo esto está muy bien y funciona. Pero... vamos a darle una vuelta de tuerca mediante una serie de aproximaciones para mejorarlo:

La función factorial está muy bien así, aunque seguramente alguno podría exigir un poco más. Tampoco se puede optimizar mucho, pero vamos a aprovechar para presentar una construcción curiosa del lenguaje que conocí usando Ruby y luego me enteré de que en Python también estaba disponible:

def factorial(n):
    """Devuelve el factorial del argumento recibido"""
    return (1 if n == 0 else n * factorial(n - 1))

En la función sumadig hay mucho margen de mejora. Básicamente lo que hacemos en la misma es:

  • Convertir el dígito en una cadena
  • Crear una lista con todos los dígitos presentes en la cadena
  • Recorrer la lista de dígitos y sumarlos
  • Devolver el resultado

Así que vamos por partes:

  • Convertir el dígito en una cadena. Chupao!
    nstr = str(n)
  • Crear una lista con todos los dígitos presentes en la cadena
    lista = []
    for i in range(len(nstr)):
        lista.append(nstr[i])
  • Esta forma de hacerlo es lógica, pero en Python ya existe una función interna que funciona de manera más eficiente y además es más fácil de aprender:
    lista = list(str(n))
  • Recorrer la lista de dígitos y sumarlos
    suma = 0
    for i in lista:
        suma += int(i)
  • Siempre que veamos una situación en la que se recorre una lista y se aplica una función a todos los elementos estamos en condiciones de usar map. Cuando veamos una situación en la que se recorren los elementos de una lista y se aplica una función de agregación (suma de todos los elementos, producto de todos los elemento, etc.) se puede usar reduce. En este caso, vamos a ver las dos funciones. Tanto map como reduce toman 2 parámetros, el primero es la función a aplicar y el segundo, la lista sobre la que se aplica. La función que queremos usar para map es convertir a entero y la de reduce es sumar. Para map usaremos la función int que convierte a entero. Para reduce creamos una función lambda para la suma y la usamos como primer parámetro.
    lista2 = map(int, lista)
    reduce (lamda x, y: x+y, lista2)
  • Si lo ponemos todo en una línea:
    reduce(lambda x, y: x+y, map(int, list(str(n))))
  • Devolver el resultado
    return reduce(lambda x, y: x+y, map(int, list(str(n))))

En resumen, la función sumadig nos quedaría así:

def sumadig(n):
    """Devuelve la suma de los dígitos del argumento recibido"""

    return reduce(lambda x, y: x+y, map(int, list(str(n))))

Está bastante compacto el código fuente y a lo mejor resulta poco comprensible pero creo que es un buen ejercicio para comprender el funcionamiento de las funciones map y reduce.

Espero que os haya gustado el artículo.

Saludos.

 

¡Votalo! 4 votos
¡Compártelo!

        

&nbps;

&nbps;

anhelido

Sobre anhelido

Soy Diplomado en Informática por la E.U.I. de la Universidad Politécnica de Madrid, lo equivale ahora a Ingeniero Técnico en Informática de Sistemas. He estado trabajando en muchos proyectos en varias empresas y con entornos muy diferentes, desde aplicaciones bancarias a sistemas de emergencias, pasando por simuladores de vuelo. Me gusta aprender de muchos campos diferentes, aunque al final uno siempre tiene la sensación de ser aprendiz de todo y maestro de nada.

 
Regístrate o haz login para participar.
¿Todavía no conoces debugmodeon?
debugmodeon es la red social para profesionales de la informática
descubre debugmodeon
 

7 comentarios en "Map y Reduce en Python"

quintesse
quintesse escribió
hace 1 años

#1   

Muy claro, me ha gustado, puedes escribir mas artículos como este jeje

Pero no podía dejar y intentar hacer lo mismo en Scala para ver como se quedaría:

def sumadig(n: Int) = n.toString.elements.map(.asDigit).reduceLeft( + _)

Tampoco esta nada mal!

 

anhelido
anhelido escribió
hace 1 años

#2   

Pues efectivamente, nada mal y además se entiende muy bien aun sin saber Scala.

 

cort084
cort084 escribió
hace 1 años

#3   

La verdad es que es interesante la forma de resolver ese problema, me recuerda bastante a Haskell.

 

gimenete
gimenete escribió
hace 1 años

#4   

Buen artículo!

Sugerencia: en Python se puede simplificar un poco más usando la función sum()

return sum(map(int, list(str(n))))

Pero bueno, como el artículo va de map-reduce, como ejemplo es mejor como está, usando reduce() :)

 

Venkman
Venkman escribió
hace 1 años

#5   

Muy interesante como ejercicio para usar y explicar map y reduce.

Mi retorcida mente, sin embargo, está tentándome con una serie de temas perpendiculares sobre Python... Por ejemplo, ¿cómo podríamos mejorar ese factorial? Sin entrar en temas de rendimiento, usar una solución recursiva ya tiene la limitación del "maximum recursion depth exceeded" si en lugar de calcular 100! calculamos 1000! (Sí, sí, podemos aumentar ese límite, pero siempre está ahí como límite). Así que se me ocurre que podríamos hacerlo más funcional precisamente usando reduce...

factorial = lambda n:reduce(lambda a,b:a*(b+1),range(n),1)
print sumadig(factorial(10000))

Así, en unas pruebas rápidas, no veo mucha diferencia de rendimiento. Pero a similar rendimiento, esta forma funcional no tiene problemas de recursión. Podemos fácilmente pedirle el factorial de 10000 mientras que a la forma original no he conseguido sacarle ni el de 5000 (incluso aumentando el límite de recursión).

 

anhelido
anhelido escribió
hace 1 años

#6   

Si ya sabía yo que me ayudarías a aprender Python! :P

 

chemacortes
chemacortes escribió
hace 9 meses

#7   

Ahora mismo, en python se están abandonado tanto map como reduce. En su lugar se usan los "iteradores" que dan bastante más juego. Por ejemplo, para sumar los dígitos del factorial:

print sum(int(digito) for digito in str(factorial(10000)))

Una de las ventajas es que no hace falta convertir el número a lista, ya que las cadenas de caracteres son ya en sí mismas "secuencias" por las que se puede iterar.

 
 
 
 

© Copyright 2008-2009 debug_mode=ON | Aviso legal | Contacto | FAQ | ¿Quiénes somos? |