Каков аналог функции Haskell zipWith в Python?
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
Каков аналог функции Haskell zipWith в Python?
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
Вы можете создать свой, если хотите, но в Python мы в основном делаем
list_c = [ f(a,b) for (a,b) in zip(list_a,list_b) ]
поскольку Python не является функционально функциональным. Это просто помогает поддерживать несколько удобных идиом.
map()
map(operator.add, [1, 2, 3], [3, 2, 1])
Хотя обычно используется LC с zip()
.
[x + y for (x, y) in zip([1, 2, 3], [3, 2, 1])]
Вы можете использовать карту:
>>> x = [1,2,3,4]
>>> y = [4,3,2,1]
>>> map(lambda a, b: a**b, x, y)
[1, 8, 9, 4]
ленивый zipWith
с itertools:
import itertools
def zip_with(f, *coll):
return itertools.starmap(f, itertools.izip(*coll))
Эта версия обобщает поведение zipWith
с любым числом итераций.
Как и другие, упомянутые карты и zip могут помочь вам размножить функции zipWith, как в Haskel.
Как правило, вы можете применить определенный двоичный оператор или некоторую двоичную функцию в двух списках. Пример замены Haskel zipWith с помощью Python map/zip
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]
>>>
Существуют другие варианты zipWith aka zipWith3, zipWith4.... zipWith7. Чтобы воспроизвести этих функционалистов, вы можете использовать izip и imap вместо zip и map.
>>> [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]
Как вы можете видеть, вы можете использовать любое количество желаемого списка, и вы все равно можете использовать ту же процедуру.
Я знаю, что это старый вопрос, но...
Уже было сказано, что типичный способ python будет чем-то вроде
results = [f(a, b) for a, b in zip(list1, list2)]
и поэтому, видя такую строку в вашем коде, большинство pythonistas прекрасно понимают.
Также был показан (я думаю) простой ленивый пример:
import itertools
def zipWith(f, *args):
return itertools.starmap(f, itertools.izip(*args))
но я считаю, что starmap возвращает итератор, поэтому вы не сможете индексировать или многократно повторять то, что возвращает эта функция.
Если вы не особенно обеспокоены лени и/или вам нужно индексировать или прокручивать свой новый список несколько раз, это, вероятно, является общей целью, как вы могли бы получить:
def zipWith(func, *lists):
return [func(*args) for args in zip(*lists)]
Не то, чтобы вы не могли сделать это с ленивой версией, но вы также можете вызвать эту функцию, если вы уже создали список списков.
results = zipWith(func, *lists)
или просто как обычно:
results = zipWith(func, list1, list2)
Как-то этот вызов функции выглядит проще и проще, чем версия для понимания списка.
Глядя на это, это странно напоминает другую вспомогательную функцию, которую я часто пишу:
def transpose(matrix):
return zip(*matrix)
который затем можно было бы записать так:
def transpose(matrix):
return zipWith(lambda *x: x, *matrix)
Не самая лучшая версия, но мне всегда интересно, как при написании родовых функций в функциональном стиле я часто нахожусь в "О. Это просто более общая форма функции, которую я уже писал ранее."