Функциональное программирование против понимания списка - PullRequest
0 голосов
/ 28 апреля 2018

Марк Лутц в своей книге «Изучение Python» приводит пример:

>>> [(x,y) for x in range(5) if x%2==0 for y in range(5) if y%2==1]
[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
>>>

чуть позже он отмечает, что «эквивалент карты и фильтра» этого возможен, хотя и сложен и вложен.

Ближайший, с которым я закончил, следующий:

>>> list(map(lambda x:list(map(lambda y:(y,x),filter(lambda x:x%2==0,range(5)))), filter(lambda x:x%2==1,range(5))))
[[(0, 1), (2, 1), (4, 1)], [(0, 3), (2, 3), (4, 3)]]
>>> 

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

Ответы [ 4 ]

0 голосов
/ 22 марта 2019

Range support step аргумент, поэтому я придумаю это решение, используя itertools.chain.from_iterable, чтобы сгладить внутренний список:

from itertools import chain
list(chain.from_iterable(
    map(
        lambda x:           
            list(map(lambda y: (x, y), range(1, 5, 2))),
        range(0, 5, 2)
        )
))    

Выход:

Out[415]: [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
0 голосов
/ 28 апреля 2018

Примечание для добавления к @ объяснению Касрамвда .

Читаемость важна в Python . Это одна из особенностей языка. Многие считают, что понимание списка возможно только для .

Иногда, однако, особенно когда вы работаете с несколькими итерациями условий, яснее отделить ваши критерии от логики . В этом случае использование функционального метода может быть предпочтительным.

from itertools import product

def even_and_odd(vals):
    return (vals[0] % 2 == 0) and (vals[1] %2 == 1)

n = range(5)

res = list(filter(even_and_odd, product(n, n)))
0 голосов
/ 28 апреля 2018

Я думаю, что единственная запутанная часть в выражении [(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1] заключается в том, что там скрытая flatten операция скрыта.

Давайте сначала рассмотрим упрощенную версию выражения:

def even(x):
    return x % 2 == 0

def odd(x):
    return not even(x)

c = map(lambda x: map(lambda y: [x, y], 
                      filter(odd, range(5))), 
        filter(even, range(5)))

print(c)
# i.e. for each even X we have a list of odd Ys:
# [
#   [[0, 1], [0, 3]],
#   [[2, 1], [2, 3]],
#   [[4, 1], [4, 3]]
# ]

Однако нам нужен примерно такой же, но уплощенный список [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)].

Из официальных документов Python мы можем взять пример функции flatten:

from itertools import chain
flattened = list(chain.from_iterable(c))  # we need list() here to unroll an iterator
print(flattened)

Что в основном эквивалентно следующему выражению понимания списка:

flattened = [x for sublist in c for x in sublist]
print(flattened)

# ... which is basically an equivalent to:
# result = []
# for sublist in c:
#   for x in sublist:
#     result.append(x)
0 голосов
/ 28 апреля 2018

Один важный момент, который вы должны заметить, заключается в том, что ваше понимание вложенного списка имеет порядок O (n 2 ). Это означает, что он зацикливается на продукте двух диапазонов. Если вы хотите использовать map и filter, вы должны создать все комбинации. Вы можете сделать это после или до фильтрации, но что бы вы ни делали, у вас не будет всех этих комбинаций с этими двумя функциями, если вы не измените диапазоны и / или не измените что-то еще.

Один полностью функциональный подход заключается в использовании itertools.product() и filter следующим образом:

In [16]: from itertools import product

In [17]: list(filter(lambda x: x[0]%2==0 and x[1]%2==1, product(range(5), range(5))))
Out[17]: [(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]

Также обратите внимание, что использование понимания вложенного списка с двумя итерациями в основном более читабельно, чем множественные функции map / filter. А что касается производительности с использованием встроенных функций, это быстрее, чем понимание списков, когда ваша функция просто встроена, так что вы можете быть уверены, что все они работают на уровне C. Когда вы разрываете цепочку чем-то вроде lambda функции, которая является операцией Python / более высокого уровня, ваш код не будет быстрее, чем понимание списка.

...