Использование карты Python и других функциональных инструментов - PullRequest
127 голосов
/ 23 марта 2009

Это довольно много, но я пытаюсь изучить / понять функциональное программирование на python. Следующий код:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, bars)

производит:

1.0 1
2.0 2
3.0 3
4.0 None
5.0 None

Q. Есть ли способ использовать карту или любые другие функциональные инструменты в Python для создания следующего без циклов и т. Д.

1.0 [1,2,3]
2.0 [1,2,3]
3.0 [1,2,3]
4.0 [1,2,3]
5.0 [1,2,3]

Просто обратите внимание, как изменится реализация, если существует зависимость между foo и bar. например,

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3,4,5]

и печать:

1.0 [2,3,4,5]
2.0 [1,3,4,5]
3.0 [1,2,4,5]
...

П.С .: Я знаю, как это сделать наивно, используя if, циклы и / или генераторы, но я хотел бы узнать, как добиться того же, используя функциональные инструменты. Это просто случай добавления оператора if в maptest или применения другой карты фильтра к столбцам внутри maptest?

Ответы [ 9 ]

194 голосов
/ 23 марта 2009

Вы знакомы с другими функциональными языками? то есть вы пытаетесь узнать, как Python выполняет функциональное программирование, или вы пытаетесь узнать о функциональном программировании и использовании Python как средства?

Кроме того, понимаете ли вы списки?

map(f, sequence)

прямо эквивалентно (*):

[f(x) for x in sequence]

На самом деле, я думаю, map() когда-то был намечен для удаления из Python 3.0 как избыточный (этого не произошло).

map(f, sequence1, sequence2)

в основном эквивалентен:

[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]

(есть разница в том, как он обрабатывает случай, когда последовательности имеют разную длину. Как вы видели, map() заполняет None, когда заканчивается одна из последовательностей, тогда как zip() останавливается, когда останавливается самая короткая последовательность )

Итак, чтобы ответить на ваш конкретный вопрос, вы пытаетесь получить результат:

foos[0], bars
foos[1], bars
foos[2], bars
# etc.

Вы можете сделать это, написав функцию, которая принимает один аргумент и печатает его, после чего следуют столбцы:

def maptest(x):
     print x, bars
map(maptest, foos)

Кроме того, вы можете создать список, который выглядит следующим образом:

[bars, bars, bars, ] # etc.

и используйте свой оригинальный тест карты:

def maptest(x, y):
    print x, y

Один из способов сделать это состоит в том, чтобы заранее составить список заранее:

barses = [bars] * len(foos)
map(maptest, foos, barses)

В качестве альтернативы, вы можете использовать модуль itertools. itertools содержит много умных функций, которые помогут вам выполнять программирование с отложенным вычислением в Python. В этом случае нам нужен itertools.repeat, который будет выводить свой аргумент бесконечно, пока вы итерируете его. Этот последний факт означает, что если вы делаете:

map(maptest, foos, itertools.repeat(bars))

вы получите бесконечный вывод, поскольку map() продолжает работать, пока один из аргументов все еще производит вывод. Однако itertools.imap аналогичен map(), но останавливается, как только останавливается кратчайшая итерация.

itertools.imap(maptest, foos, itertools.repeat(bars))

Надеюсь, это поможет: -)

(*) Это немного отличается в Python 3.0. Там map () по существу возвращает выражение генератора.

55 голосов
/ 23 марта 2009

Самый простой способ - не передавать bars через различные функции, а получить к нему доступ непосредственно из maptest:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo):
    print foo, bars

map(maptest, foos)

С вашей оригинальной функцией maptest вы также можете использовать лямбда-функцию в map:

map((lambda foo: maptest(foo, bars)), foos)
30 голосов
/ 23 марта 2009

Вот решение, которое вы ищете:

>>> foos = [1.0, 2.0, 3.0, 4.0, 5.0]
>>> bars = [1, 2, 3]
>>> [(x, bars) for x in foos]
[(1.0, [1, 2, 3]), (2.0, [1, 2, 3]), (3.0, [1, 2, 3]), (4.0, [1, 2, 3]), (5.0, [
1, 2, 3])]

Я бы порекомендовал использовать понимание списка (часть [(x, bars) for x in foos]) вместо использования map, поскольку это позволяет избежать накладных расходов при вызове функции на каждой итерации (что может быть очень значительным). Если вы просто собираетесь использовать его в цикле for, вы получите лучшую скорость, используя понимание генератора:

>>> y = ((x, bars) for x in foos)
>>> for z in y:
...     print z
...
(1.0, [1, 2, 3])
(2.0, [1, 2, 3])
(3.0, [1, 2, 3])
(4.0, [1, 2, 3])
(5.0, [1, 2, 3])

Разница в том, что генератор понимает лениво загружен .

ОБНОВЛЕНИЕ В ответ на этот комментарий:

Конечно, вы знаете, что вы не копируете бары, все записи - это один и тот же список баров. Поэтому, если вы изменяете какой-либо из них (включая оригинальные столбцы), вы изменяете их все.

Полагаю, это верный момент. Есть два решения для этого, о которых я могу думать. Наиболее эффективным, вероятно, является что-то вроде этого:

tbars = tuple(bars)
[(x, tbars) for x in foos]

Поскольку кортежи являются неизменяемыми, это предотвратит изменение баров по результатам этого понимания списка (или понимания генератора, если вы пойдете по этому пути). Если вам действительно нужно изменить каждый из результатов, вы можете сделать это:

from copy import copy
[(x, copy(bars)) for x in foos]

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

20 голосов
/ 23 марта 2009

Функциональное программирование - это создание кода без побочных эффектов.

map - это абстракция преобразования функционального списка. Вы используете это, чтобы взять последовательность чего-то и превратить это в последовательность чего-то другого.

Вы пытаетесь использовать его в качестве итератора. Не делай этого. :)

Вот пример того, как вы можете использовать карту, чтобы построить нужный список. Есть более короткие решения (я бы просто использовал понимания), но это поможет вам понять, какая карта работает лучше:

def my_transform_function(input):
    return [input, [1, 2, 3]]

new_list = map(my_transform, input_list)

Обратите внимание, что на данный момент вы сделали только манипуляции с данными. Теперь вы можете распечатать его:

for n,l in new_list:
    print n, ll

- Я не уверен, что вы подразумеваете под «без петель». fp не о том, чтобы избегать циклов (вы не можете просмотреть каждый элемент в списке, не посещая каждый). Речь идет о том, чтобы избежать побочных эффектов, и, следовательно, писать меньше ошибок.

12 голосов
/ 23 марта 2009
>>> from itertools import repeat
>>> for foo, bars in zip(foos, repeat(bars)):
...     print foo, bars
... 
1.0 [1, 2, 3]
2.0 [1, 2, 3]
3.0 [1, 2, 3]
4.0 [1, 2, 3]
5.0 [1, 2, 3]
11 голосов
/ 23 марта 2009
import itertools

foos=[1.0, 2.0, 3.0, 4.0, 5.0]
bars=[1, 2, 3]

print zip(foos, itertools.cycle([bars]))
6 голосов
/ 23 марта 2009

Вот обзор параметров для функции map(function, *sequences):

  • function - название вашей функции.
  • sequences - любое количество последовательностей, которые обычно являются списками или кортежами. map будет итерировать их одновременно и присвоит текущие значения function. Вот почему количество последовательностей должно равняться количеству параметров вашей функции.

Звучит так, будто вы пытаетесь выполнить итерацию для некоторых параметров function, но оставляете другие постоянными, и, к сожалению, map не поддерживает это. Я нашел старое предложение добавить такую ​​функцию в Python, но конструкция карты настолько чиста и устоялась, что я сомневаюсь, что что-то подобное когда-либо будет реализовано.

Используйте обходной путь, такой как глобальные переменные или списки, как предлагали другие.

0 голосов
/ 06 сентября 2013

Как насчет этого:

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest(foo, bar):
    print foo, bar

map(maptest, foos, [bars]*len(foos))
0 голосов
/ 23 марта 2009

Будет ли это сделать?

foos = [1.0,2.0,3.0,4.0,5.0]
bars = [1,2,3]

def maptest2(bar):
  print bar

def maptest(foo):
  print foo
  map(maptest2, bars)

map(maptest, foos)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...