Эквивалент понимания списка с использованием карты и фильтра

#python #python-3.x #list #list-comprehension #map-function

#python #python-3.x #Список #понимание списка #карта-функция

Вопрос:

Я хочу написать этот код, используя карту и / или функцию фильтра. Он возвращает индексы элементов в списке при условии, что сумма соответствует цели

Для этого я использовал понимание списка, но не вижу, как получить второй цикл for в функцию map / filter. Я не уверен в синтаксисе, который будет использоваться, если я определю свою собственную функцию для аргумента функции функции map / filter

 num = [2,5,7,11,6,15,3,4]
tgt= 9
[num.index(x) for x in num for y in num if x   y == tgt]
  

Результаты:

 [0, 1, 2, 4, 6, 7]
  

Ответ №1:

Поскольку оба filter и map работают над отдельными элементами в последовательности, вам придется просматривать свою логику с точки зрения каждого элемента в списке, а не комбинации элементов, а это означает, что вам нужно перефразировать выражения, используемые в вашем понимании списка, как функции отдельных элементов. Поэтому вместо условия фильтра x y == tgt полезно просматривать его как x == tgt - y , где y также должен быть элемент в num списке, чтобы ваше понимание списка можно было переписать как:

 [num.index(x) for x in num if x in {tgt - y for y in num}]
  

При таком эквивалентном понимании списка становится ясно, что для реализации условия фильтра потребуется создать набор путем сопоставления каждого элемента с num его разницей с tgt , что можно сделать с tgt.__sub__ помощью метода, и проверить каждый элемент x num , является ли он членом набора, что можно сделать с помощьюметод set __contains__ и, наконец, сопоставьте отфильтрованную последовательность с num.index для вывода индекса каждого соответствующего элемента:

 list(map(num.index, filter(set(map(tgt.__sub__, num)).__contains__, num)))
  

Это возвращает:

 [0, 1, 2, 4, 6, 7]
  

Комментарии:

1. Используйте set вместо list здесь list(map(tgt.__sub__, num)) для лучшего поиска

2. @blhsing Можете ли вы объяснить внутреннюю работу вашего решения?

Ответ №2:

Попробуйте это! Вы можете использовать itertools.product для получения комбинаций каждого из них. Затем отфильтруйте список комбинаций для элементов, сумма которых равна tgt . Затем сопоставьте лямбда-выражение с этими результатами, чтобы получить индекс первого элемента в этих комбинациях.

 list(map(lambda x: num.index(x[0]), (filter(lambda x: sum(x) == tgt, itertools.product(num, repeat=2)))))
  

Ответ №3:

Двойной цикл может быть кодом в виде itertools.product :

 >>> list(map(lambda x: num.index(x[0]), filter(lambda x: sum(x) == tgt, itertools.product(num, num))))
[0, 1, 2, 4, 6, 7]
  

Давайте разберем код:

 filter(lambda x: sum(x) == tgt, itertools.product(num, num))
  

itertools.product возвращает итератор кортежей с элементами в num, (x, y) аналогичный используемому вами вложенному циклу.
мы фильтруем, какому из этих кортежей равно суммирование tgt , использование sum здесь является лучшим выбором, но обратите внимание, что это будет то же x[0] x[1] самое, что и (помните, что мы даем кортежи, подобные (2, 2) этой функции).

После того, как мы отфильтровали, мы применяем для каждого из тех кортежей, которые остаются функцией num.index , поскольку у нас есть кортежи, нам нужно использовать только одно из значений, помните, что первое соответствует вложенному циклу for x , следовательно num.index(x[0])

Комментарии:

1. Спасибо, можете ли вы объяснить внутреннюю работу. Почему вы используете функции сокращения, т.е.(sum() и product()) и num.index(x[0]) ?

Ответ №4:

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

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

Как указывали другие, вы можете использовать itertools.product , но вместо продукта num с самим собой, сделайте self product of range(len(num)) .

Использование map и filter :

 from operator import itemgetter
from itertools import product

res = map(
    itemgetter(0), 
    filter(
        lambda c: num[c[0]] num[c[1]] == tgt, 
        product(range(len(num)),range(len(num)))
    )
)
print(list(res))
#[0, 1, 2, 4, 6, 7]
  

Внутренняя filter функция фильтрует все пары чисел от 0 до длины num минус один, для которых значения num в соответствующих индексах равны целевому. Поскольку продукт возвращает пару индексов, и вас интересует только первое значение, map результат filter с itemgetter(0) , чтобы получить первый элемент.

Более компактно, как понимание списка:

 [i for i, j in product(range(len(num)), range(len(num))) if num[i]   num[j] == tgt]