Usando el mapa de Python y otras herramientas funcionales

127

Esto es bastante simple, pero estoy tratando de aprender / entender la programación funcional en python. El siguiente código:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, bars)

produce:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

P. ¿Hay alguna manera de usar map o cualquier otra herramienta funcional en python para producir lo siguiente sin bucles, etc.

1.0 [1,2,3]
2.0 [1,2,3]
3.0 [1,2,3]
4.0 [1,2,3]
5.0 [1,2,3]

Solo como nota al margen, ¿cómo cambiaría la implementación si hubiera una dependencia entre foo y bar? p.ej

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]

e imprimir:

1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...

PD: Sé cómo hacerlo ingenuamente usando if, bucles y / o generadores, pero me gustaría aprender cómo lograrlo usando herramientas funcionales. ¿Es solo un caso de agregar una instrucción if a maptest o aplicar otro mapa de filtro a las barras internamente dentro de maptest?

eusoubrasileiro
fuente
Gracias chicos. Debo admitir que estoy tratando de aprender los conceptos de programación funcional a través de Python.
1
Un buen tutorial para esto aquí: dreamsyssoft.com/python-scripting-tutorial/…
Rocky Pulley

Respuestas:

54

La forma más fácil sería no pasar barspor las diferentes funciones, sino acceder directamente desde maptest:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo):
    print foo, bars

map(maptest, foos)

Con su maptestfunción original , también podría usar una función lambda en map:

map((lambda foo: maptest(foo, bars)), foos)
algo
fuente
malo cuando los bares vienen de la lista
Phyo Arkar Lwin
59
Esta solución va directamente en contra de los principios de programación funcional que el OP quiere intentar aprender. Una regla fundamental en la programación funcional es que cada vez que llama a una función con los mismos argumentos, SIEMPRE obtiene el mismo resultado. Esto evita un nido de bichos de víboras introducido por tener un estado global. Como maptest depende de una definición externa de barras, este principio está roto.
image_doctor
3
Estimado desbordamiento de pila, ya que le gusta cerrar preguntas y moderadamente, ¿por qué no desmarca esta pregunta como respuesta y marca la respuesta correcta como respuesta? Un saludo, nosotros.
Bahadir Cambel
1
@image_doctor, en FP está perfectamente bien acceder a la constante global (hay que considerarla como función nular)
Peter K
1
La moderación de @BahadirCambel Stack Overflow puede ser dura, a veces, pero la marca de verificación siempre, y siempre, pertenecerá al OP.
wizzwizz4
194

¿Conoces otros lenguajes funcionales? es decir, ¿estás tratando de aprender cómo Python hace la programación funcional, o estás tratando de aprender acerca de la programación funcional y usar Python como vehículo?

Además, ¿entiendes las comprensiones de listas?

map(f, sequence)

es directamente equivalente (*) a:

[f(x) for x in sequence]

De hecho, creo que map()una vez se programó la eliminación de Python 3.0 como redundante (eso no sucedió).

map(f, sequence1, sequence2)

es mayormente equivalente a:

[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]

(hay una diferencia en cómo maneja el caso en el que las secuencias son de diferente longitud. Como viste, completa map()None cuando una de las secuencias se agota, mientras que se zip()detiene cuando se detiene la secuencia más corta)

Entonces, para abordar su pregunta específica, está tratando de producir el resultado:

foos[0], bars
foos[1], bars
foos[2], bars
# etc.

Puede hacer esto escribiendo una función que tome un solo argumento y lo imprima, seguido de barras:

def maptest(x):
     print x, bars
map(maptest, foos)

Alternativamente, puede crear una lista que se vea así:

[bars, bars, bars, ] # etc.

y usa tu prueba de mapa original:

def maptest(x, y):
    print x, y

Una forma de hacerlo sería crear explícitamente la lista de antemano:

barses = [bars] * len(foos)
map(maptest, foos, barses)

Alternativamente, puede tirar del itertoolsmódulo. itertoolscontiene muchas funciones inteligentes que lo ayudan a realizar una programación de evaluación perezosa de estilo funcional en python. En este caso, queremos itertools.repeat, que generará su argumento de forma indefinida a medida que lo repita. Este último hecho significa que si haces:

map(maptest, foos, itertools.repeat(bars))

obtendrá una salida infinita, ya que map()continúa mientras uno de los argumentos siga produciendo. Sin embargo, itertools.imapes igual map(), pero se detiene tan pronto como se detiene el iterativo más corto.

itertools.imap(maptest, foos, itertools.repeat(bars))

Espero que esto ayude :-)

(*) Es un poco diferente en python 3.0. Allí, map () esencialmente devuelve una expresión generadora.

John Fouhy
fuente
Entonces, ¿entiendo correctamente que, a diferencia del mapa, itertools.imap(f, sequence1, sequence2)realmente es equivalente a [f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]?
Jon Coombs
Al probar un poco, veo que devuelve un objeto itertools.imap, así que tal vez esto sería más 'equivalente':list(itertools.imap(f, sequence1, sequence2))
Jon Coombs
Esta debería ser la respuesta aprobada.
Rob Grant
30

Aquí está la solución que estás buscando:

>>> foos = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> bars = [1, 2, 3]
>>> [(x, bars) for x in foos]
[(1.0, [1, 2, 3]), (2.0, [1, 2, 3]), (3.0, [1, 2, 3]), (4.0, [1, 2, 3]), (5.0, [
1, 2, 3])]

Recomiendo usar una comprensión de la lista (la [(x, bars) for x in foos]parte) sobre el uso del mapa, ya que evita la sobrecarga de una llamada a la función en cada iteración (que puede ser muy significativo). Si solo lo va a usar en un bucle for, obtendrá mejores velocidades utilizando un generador de comprensión:

>>> y = ((x, bars) for x in foos)
>>> for z in y:
...     print z
...
(1.0, [1, 2, 3])
(2.0, [1, 2, 3])
(3.0, [1, 2, 3])
(4.0, [1, 2, 3])
(5.0, [1, 2, 3])

La diferencia es que la comprensión del generador está perezosamente cargada .

ACTUALIZACIÓN En respuesta a este comentario:

Por supuesto, usted sabe que no copia barras, todas las entradas son la misma lista de barras. Entonces, si modifica cualquiera de ellos (incluidas las barras originales), los modificará a todos.

Supongo que este es un punto válido. Hay dos soluciones a esto en las que puedo pensar. El más eficiente es probablemente algo como esto:

tbars = tuple(bars)
[(x, tbars) for x in foos]

Dado que las tuplas son inmutables, esto evitará que las barras se modifiquen a través de los resultados de esta comprensión de la lista (o comprensión del generador si sigue esa ruta). Si realmente necesita modificar todos y cada uno de los resultados, puede hacer esto:

from copy import copy
[(x, copy(bars)) for x in foos]

Sin embargo, esto puede ser un poco costoso tanto en términos de uso de memoria como de velocidad, por lo que lo recomendaría a menos que realmente necesite agregar a cada uno de ellos.

Jason Baker
fuente
1
Por supuesto, usted sabe que no copia barras, todas las entradas son la misma lista de barras. Entonces, si modifica cualquiera de ellos (incluidas las barras originales), los modificará a todos.
vartec
20

La programación funcional se trata de crear código libre de efectos secundarios.

map es una abstracción de transformación de lista funcional. Lo usas para tomar una secuencia de algo y convertirlo en una secuencia de otra cosa.

Estás intentando usarlo como un iterador. No hagas eso. :)

Aquí hay un ejemplo de cómo puede usar el mapa para crear la lista que desea. Hay soluciones más cortas (solo usaría comprensiones), pero esto lo ayudará a comprender qué mapa funciona un poco mejor:

def my_transform_function(input):
    return [input, [1, 2, 3]]

new_list = map(my_transform, input_list)

Tenga en cuenta que en este punto, solo ha realizado una manipulación de datos. Ahora puedes imprimirlo:

for n,l in new_list:
    print n, ll

- No estoy seguro de lo que quieres decir con "sin bucles". fp no se trata de evitar bucles (no puede examinar cada elemento de una lista sin visitar cada uno). Se trata de evitar los efectos secundarios, por lo tanto, escribir menos errores.

Dustin
fuente
12
>>> from itertools import repeat
>>> for foo, bars in zip(foos, repeat(bars)):
...     print foo, bars
... 
1.0 [1, 2, 3]
2.0 [1, 2, 3]
3.0 [1, 2, 3]
4.0 [1, 2, 3]
5.0 [1, 2, 3]
Roberto Bonvallet
fuente
11
import itertools

foos=[1.0, 2.0, 3.0, 4.0, 5.0]
bars=[1, 2, 3]

print zip(foos, itertools.cycle([bars]))
Ignacio Vazquez-Abrams
fuente
Este es el más fácil y correctamente funcional. acepta esto como respuesta
Phyo Arkar Lwin
1
Esto es solo código. No hay explicacion. Muchos usuarios no entienden lo que significa esta respuesta. @PhyoArkarLwin
ProgramFast
6

Aquí hay una descripción general de los parámetros de la map(function, *sequences)función:

  • function es el nombre de tu función.
  • sequenceses cualquier número de secuencias, que generalmente son listas o tuplas. mapiterará sobre ellos simultáneamente y dará los valores actuales a function. Es por eso que el número de secuencias debería ser igual al número de parámetros de su función.

Parece que estás intentando iterar para algunos de function los parámetros pero mantener otros constantes, y desafortunadamente mapno es compatible con eso. Encontré una vieja propuesta para agregar dicha característica a Python, pero la construcción del mapa es tan limpia y bien establecida que dudo que alguna vez se implemente algo así.

Utilice una solución alternativa como las variables globales o comprensiones de listas, como lo han sugerido otros.

Nikhil Chelliah
fuente
0

¿Esto lo haría?

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest2(bar):
  print bar

def maptest(foo):
  print foo
  map(maptest2, bars)

map(maptest, foos)
Chris
fuente
1
Es posible que desee llamar al parámetro para maptest2 () algo así como 'barras'. El singular barimplica que está recibiendo un valor iterado, cuando realmente desea la lista completa.
Nikhil Chelliah el
1
En realidad, está recibiendo un valor iterado, creo.
Chris
0

Qué tal esto:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, [bars]*len(foos))
sun.huaiyu
fuente