tutorial - zip ¿Con analógico en Python?




python graph function (4)

¿Cuál es el análogo de la función zipWith de Haskell en Python?

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]

En general, como otros han mencionado, el mapa y el zip pueden ayudarlo a replicar la funcionalidad de zipWith como en Haskel.

En general, puede aplicar un operador binario definido o alguna función binaria en dos listas. Un ejemplo para reemplazar un zip de Haskel con el mapa / zip de Python

Input: zipWith (+) [1,2,3] [3,2,1] 
Output: [4,4,4] 

>>> map(operator.add,[1,2,3],[4,3,2])
[5, 5, 5]
>>> [operator.add(x,y) for x,y in zip([1,2,3],[4,3,2])]
[5, 5, 5]
>>> 

Hay otras variaciones de zipWith aka zipWith3, zipWith4 .... zipWith7. Para replicar estos funcionalistas es posible que desee utilizar izip y imap en lugar de zip y mapa.

>>> [x for x in itertools.imap(lambda x,y,z:x**2+y**2-z**2,[1,2,3,4],[5,6,7,8],[9,10,11,12])]
>>> [x**2+y**2-z**2 for x,y,z in itertools.izip([1,2,3,4],[5,6,7,8],[9,10,11,12])]
[-55, -60, -63, -64] 

Como puede ver, puede operar con cualquier cantidad de listas que desee y aún puede usar el mismo procedimiento.


Puedes crear el tuyo, si lo deseas, pero en Python lo hacemos principalmente

list_c = [ f(a,b) for (a,b) in zip(list_a,list_b) ] 

ya que Python no es intrínsecamente funcional. Simplemente sucede que admite algunos modismos de conveniencia.


Sé que esta es una vieja pregunta, pero ...

Ya se ha dicho que la forma típica de python sería algo así como

results = [f(a, b) for a, b in zip(list1, list2)]

Entonces, al ver una línea así en tu código, la mayoría de los pitonistas lo entenderán bien.

También ha habido un ejemplo (creo) puramente perezoso que se muestra:

import itertools

def zipWith(f, *args):
    return itertools.starmap(f, itertools.izip(*args))

pero creo que starmap devuelve un iterador, por lo que no podrá indexar, o ir varias veces a lo que devolverá esa función.

Si no está particularmente interesado en la pereza y / o necesita indexar o recorrer repetidas veces su nueva lista, este es probablemente el objetivo más general que podría obtener:

def zipWith(func, *lists):
    return [func(*args) for args in zip(*lists)]

No es que no puedas hacerlo con la versión perezosa, pero también podrías llamar a esa función como si ya hubieras creado tu lista de listas.

results = zipWith(func, *lists)

o simplemente como normal, como:

results = zipWith(func, list1, list2)

De alguna manera, esa llamada a la función simplemente parece más simple y más fácil de asimilar que la versión de la lista de comprensión.

Al mirar eso, esto parece extrañamente reminiscente de otra función auxiliar que suelo escribir:

def transpose(matrix):
    return zip(*matrix)

que luego podría escribirse como:

def transpose(matrix):
    return zipWith(lambda *x: x, *matrix)

No es realmente una versión mejor, pero siempre me resulta interesante cómo cuando escribo funciones genéricas en un estilo funcional, a menudo me encuentro yendo, "Oh. Esa es solo una forma más general de una función que ya he escrito antes".


Un zipWith flojo con itertools:

import itertools

def zip_with(f, *coll):
    return itertools.starmap(f, itertools.izip(*coll))

Esta versión generaliza el comportamiento de zipWith con cualquier número de iterables.





haskell