Вот не-itertools подход для этой ситуации.
Во-первых, представьте, что существует версия functools.reduce
, которая принимает 3 элемента из итерируемого одновременно. Давайте назовем эту гипотетическую функцию reduce3
.
Если бы это существовало, мы могли бы сделать что-то вроде:
reduce3(lambda a, b, c: (a+b)*c, numbers)
Если бы мы просмотрели промежуточные результаты этой операции, у нас было бы что-то вроде:
1, 2, 3, 4, 5, 6 # Initial list
9, 4, 5, 6 # Step 1
65, 6 # Step 2
(65 + 6) * ?? # Step 3
Так что это почти то, что мы хотим, за исключением того, что на третьем шаге нет 3-го элемента, на который нужно умножиться. Фактически, это произойдет для любого чётного списка. Хорошо, тогда давайте просто добавим 1
к списку, если его длина равна:
if not len(numbers) % 2:
numbers.append(1)
После этого третьим шагом будет:
(65 + 6)*1
Что дает правильный ответ 71.
К сожалению, этой магической функции не существует. Но мы можем изменить исходный список, чтобы имитировать эту функцию. Нам просто нужно взять список чисел и сгруппировать последовательные пары чисел, не включая первый элемент, в кортежи. Также, если список четной длины, нам нужно добавить элемент 1
в конец.
По сути, давайте напишем функцию preprocess()
, чтобы превратить [1, 2, 3, 4, 5, 6]
в [1, (2, 3), (4, 5), (6, 1)]
.
def preprocess(myList):
my_output = [myList[0], *zip(numbers[1::2], numbers[2::2])]
if not len(myList) % 2:
my_output.append((myList[-1], 1))
return my_output
print(preprocess(numbers))
#[1, (2, 3), (4, 5), (6, 1)]
Теперь мы можем reduce
обработанный список:
from functools import reduce
result = reduce(lambda a, b: (a+b[0])*b[1], preprocess(numbers))
print(result)
#71
reducer
принимает 2 входа - число и кортеж. Он добавляет число к первому элементу кортежа и умножает результат на второй. В результате получается другое число, которое затем передается следующей операции сокращения.
Обновление
Вот общая реализация reduceN
. N
определяется длиной переданных функций, поэтому ее можно обобщить для любого числа функций.
from itertools import islice # couldn't get away from itertools this time
def reduceN(functions, iterable, initializer=None):
it = iter(iterable)
n = len(functions)
if initializer is None:
value = next(it)
else:
value = initializer
elements = list(islice(it, n))
while(elements):
for fn, el in zip(functions, elements):
value = fn(value, el)
elements = list(islice(it, n))
return value
Мы можем использовать это для циклического применения любого количества функций. Итак, оригинальный пример:
from operator import add, mul
numbers = [1, 2, 3, 4, 5, 6]
functions = [add, mul]
print(reduceN(functions, numbers))
#71
И если мы удалим последний элемент из numbers
:
print(reduceN(functions=functions, iterable=[1, 2, 3, 4, 5]))
#65