it-roy-ru.com

Использование python map и других функциональных инструментов

Это довольно ноль, но я пытаюсь изучить/понять функциональное программирование на 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

В. Есть ли способ использовать карту или любые другие функциональные инструменты в 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]
...

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

127
chiraj

Самый простой способ - не передавать 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)
55
sth

Вы знакомы с другими функциональными языками? то есть вы пытаетесь узнать, как 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.

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

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 () по существу возвращает выражение генератора.

194
John Fouhy

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

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

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

UPDATE В ответ на этот комментарий:

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

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

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

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

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

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

30
Jason Baker

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

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

20
Dustin
>>> 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]
12
Roberto Bonvallet
import itertools

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

print Zip(foos, itertools.cycle([bars]))
11
Ignacio Vazquez-Abrams

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

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

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

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

6
Nikhil Chelliah

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

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
sun.huaiyu

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

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)
0
Chris