zip с аналогом в питоне?

Что является аналогом функции zipWith из Haskell в Python?

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

6 ответов

Решение

Вы можете создать свой, если хотите, но в 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])

Хотя ЛК с 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.

Обычно вы можете применить определенный двоичный оператор или некоторую двоичную функцию к двум спискам. Например, чтобы заменить zipWith Haskel на карту /zip 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]
>>> 

Существуют другие варианты zipWith 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)

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

Другие вопросы по тегам