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