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

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

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

Ответ 1

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

Ответ 2

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

Ответ 3

Здесь решение, которое вы ищете:

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

import itertools

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

print zip(foos, itertools.cycle([bars]))

Ответ 7

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

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

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

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

Ответ 8

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

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)

Ответ 9

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

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