(This is a rehashed, self-answered version of another question which has been closed because it was not asked well.)
I have a list of integers:
numbers = [1, 2, 3, 4, 5, 6]
My goal is to alternatively apply the sum and multiplication operators successively on these numbers to get a single result.
For example, for this input, the result is to be
((1 + 2) * 3 + 4) * 5 + 6
Which reduces to 71. Essentially, this can be broken down into:
t1 = 1 + 2
t2 = t1 * 3
t3 = t2 + 4
...
and so on.
Bonus: A solution that can generalise to more than two cyclical operations would be welcome.
Here's a non-itertools approach for this situation.
Imagine first that there was a version of functools.reduce
that took in 3 items from an iterable at a time. Let's call this hypothetical function reduce3
.
If this existed, we could do something like:
reduce3(lambda a, b, c: (a+b)*c, numbers)
If we were to view the intermediate results of this operation, we'd have something like:
1, 2, 3, 4, 5, 6 # Initial list
9, 4, 5, 6 # Step 1
65, 6 # Step 2
(65 + 6) * ?? # Step 3
So this is almost what we want, except there's no 3rd item to multiply by in Step 3. In fact, this will happen for any even length list. Well then let's just append a 1
to the list if it's length is even:
if not len(numbers) % 2:
numbers.append(1)
After this, the third step will be:
(65 + 6)*1
Which results in the correct answer of 71.
Unfortunately, this magical function does not exist. But, we can modify the original list to mimic this functionality. We just need to take the numbers list and group consecutive pairs of numbers, not including the first element, into tuples. Also, if the list is even length we need to add the element 1
to the end.
In essence, let's write a function preprocess()
to turn [1, 2, 3, 4, 5, 6]
into [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)]
Now we can reduce
the processed list:
from functools import reduce
result = reduce(lambda a, b: (a+b[0])*b[1], preprocess(numbers))
print(result)
#71
The reducer
takes 2 inputs- a number and a tuple. It adds the number to the first element of the tuple, and multiplies the result by the second. The result is another number, which is then passed to the next reduce operation.
Update
Here is a general implementation of reduceN
. The N
is determined by the length of the functions passed in, so this can be generalized to any number of functions.
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
We can use this to apply any number of functions cyclically. So the original example:
from operator import add, mul
numbers = [1, 2, 3, 4, 5, 6]
functions = [add, mul]
print(reduceN(functions, numbers))
#71
And if we remove the last element from numbers
:
print(reduceN(functions=functions, iterable=[1, 2, 3, 4, 5]))
#65