Подтвердить что ты не робот

ZipWith аналоговый в Python?

Каков аналог функции Haskell zipWith в Python?

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

Ответ 1

Вы можете создать свой, если хотите, но в Python мы в основном делаем

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

поскольку Python не является функционально функциональным. Это просто помогает поддерживать несколько удобных идиом.

Ответ 2

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])]

Ответ 3

Вы можете использовать карту:

>>> x = [1,2,3,4]
>>> y = [4,3,2,1]
>>> map(lambda a, b: a**b, x, y)
[1, 8, 9, 4]

Ответ 4

ленивый zipWith с itertools:

import itertools

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

Эта версия обобщает поведение zipWith с любым числом итераций.

Ответ 5

Как и другие, упомянутые карты и 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] 

Как вы можете видеть, вы можете использовать любое количество желаемого списка, и вы все равно можете использовать ту же процедуру.

Ответ 6

Я знаю, что это старый вопрос, но...

Уже было сказано, что типичный способ 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)

Не самая лучшая версия, но мне всегда интересно, как при написании родовых функций в функциональном стиле я часто нахожусь в "О. Это просто более общая форма функции, которую я уже писал ранее."