Python Reduce Math Equation - python

I am given a list of numbers a = [1, 2, 3, 4, 5, 6] and using only Python's reduce function, return a value. The value is calculated by:
(((...(a[0] + a[1]) * a[2] + a[3]) * a[4] + ...)
So in the above example where a = [1, 2, 3, 4, 5, 6], the answer should be ((1 + 2) * 3 + 4) * 5 + 6 = 71
I'm fairly new to Python and I'm trying to learn it:
reduce(lambda x,y: x * y, map(lambda x,y: x+y, numbers))

The tricky bit is, of course, that you have to alternate between two functions in reduce. Since you can't really keep track of the current position in the list1), in order to decide which operation to use, your best chance is to use an iterator that alternates between + and *.
You could use itertools.cycle to alternate between add and mul and use those in reduce
>>> import itertools, operator
>>> op = itertools.cycle([operator.add, operator.mul])
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: next(op)(x, y), a)
71
If you can't use those modules, you can make a generator expression alternating between + and * using lambdas in a ternary expression. The rest remains the same.
>>> op = ((lambda x,y:x+y) if i % 2 == 0 else (lambda x,y:x*y) for i in range(len(a)))
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: next(op)(x, y), a)
71
1) Well, you can, using enumerate(a), or zip(range(len(a)), a), if you prefer...
>>> a = [1, 2, 3, 4, 5, 6]
>>> reduce(lambda x, y: (y[0], x[1]+y[1]) if x[0]%2==0 else (y[0], x[1]*y[1]), enumerate(a))[1]
71
Or shorter, using tuple-unpacking in the lambdas, but this only works in Python 2.x:
>>> reduce(lambda (i,x),(j,y): (j, x+y) if i%2==0 else (j, x*y), enumerate(a))[1]

Improving the solution of Akash Patel, i have come up with this, with only reduce.
reduce(lambda acc,x: (acc+x[1] if x[0]%2 else acc*x[1]), enumerate(numbers), 1)
We need to add odd number of elements and multiply even number of elements with previous elements.

This was a tough problem because you wanted to use everything in reduce. So just use reduce twice!
reduce(lambda x, y: x + y, reduce(lambda x, y: (x[0], x[1] + y[1]) if y[0]%2==1 else (x[0], x[1]*y[1]) , enumerate(a)))

Related

How to solve lambda() takes 1 positional argument but 2 were given

I have the following code snippet but it is giving me the type error above. What I basically wanted to do is add two to every element and reduct it to a single sum, but it didnt work. How can we achieve this??
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda x:x+2, list1)
print(result)
Update:
It works when I do the following though:
list1 = [1,2,3,4]
result = functools.reduce(lambda x,y:x+y, list1)
print(result)
reduce() requires a lambda function which takes 2 arguments.
The first argument is the collector.
But here, so that +2 is applied also on the first argument, you have to give the initial value 0.
import functools
list1 = [1,2,3,4]
result = functools.reduce(lambda acc, el: acc + el + 2, list1, 0)
print(result) ## sum([3, 4, 5, 6]) = 18
You basically want to do a normal summing reduce(lambda x, y: x + y, lst) => sum(lst)
and a map(lambda x: x + 2, l) which is expressed in a more pythonic way as a list comprehension:
l = [1, 2, 3, 4]
sum([x + 2 for x in l]) # most pythonic!
## 18
sum(map(lambda x: x + 2, l))
## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, [x + 2 for x in l])
result ## 18
or:
import functools
l = [1, 2, 3 ,4]
result = functools.reduce(lambda x, y: x + y, map(lambda x: x + 2, l))
result ## 18
The callable passed to the reduce function needs to take two arguments to aggregate the current result with the next item from the given iterable:
import functools
list1 = [1, 2, 3, 4]
result = functools.reduce(lambda x, y: x + y + 2, list1, 0)
print(result)
This outputs:
18

Mapping elements using multi-line functions

Consider following example in JavaScript:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => i * i)
If I would like to call some function that does some external-state manipulation, like logging or publishing message to a topic I would simply use the brackets to get multi-line lambda:
var x = [1, 2, 3]
var y = x
.map(i => i * 2)
.map(i => {
console.log("arg received ": + i)
return i * i
})
As far as I know, such a trivial thing is impossible to do in Python because of single line lambdas and we have to use named functions which sucks because any code reader or code reviewer have to jump between function definition and function usage, even though the function is used only once.
Is there any industry standard for solving such a trivial issue?
Specifically for logging or other "void" functions, you could use or:
>>> lst = [1, 2, 3]
>>> list(map(lambda x: x*x, map(lambda x: print(x) or x, map(lambda x: x*2, lst))))
2
4
6
[4, 16, 36]
Here, a or b is equivalent to a if a else b, and since print(x) evaluates to None, which is not "truthy", the "else" part is returned after the print is executed.
Also works as a (nested) list comprehension/generator:
>>> [print(x) or x*x for x in (x*2 for x in lst)]
2
4
6
[4, 16, 36]
Whether that's good style is a different question, though.
Alternatively, and probably a bit cleaner, define a log/peek/debug function that logs the value and then returns it, to be put anywhere in your map/list-comp chain:
>>> def peek(x, f=print):
... f(x)
... return x
...
>>> [x*x for x in map(peek, (x*2 for x in lst))]
2
4
6
[4, 16, 36]
Python doesn't really have this concept, the best you can do is just to define an inner function right above where you'd use it
def my_func(i):
return i * i
y = map(my_func, map(lambda i: i * i, x))

Python mapped object becoming empty after being reduced

A = map(lambda x: x * 2, [1, 2])
z = reduce(lambda x, y: x + y, A)
print(z, list(A))
# outputs: 6 []
It's so confused that the canonical map reduce FP operation leads to the destructive end. What has happened beneath the surface?
map returns an iterator. Your reduce fully consumes that iterator, so there is nothing left afterwards. If you turn A into a list before the reduce, it works as expected:
A = list(map(lambda x: x * 2, [1, 2]))
z = reduce(lambda x, y: x + y, A)
print(z,A) #prints 6 [2, 4]

call lambda functions from list

If I have a list containing a lambda function, for example:
a = [lambda x: x * 2]
how could I call this lambda function to a list:
b = [1,2,3,4]
so that it could have the result as:
result = list(map(lambda x: x * 2, b))
and get:
[2,4,6,8]
Further, what if a contains two lambda functions, for example:
a = [lambda x:x * 2, lambda x: x-1]
How could I call these functions to list b without using for loop or list comprehension but use map()?
I hope someone could help me with it!
You can simply do
result = list(map(a[0], b))
# [2,4,6,8]
since the lambda function is the first element of a
For the second function it's the same thing
result = list(map(a[1], b))
If you want to apply all functions in one line
[list(map(a[i],b)) for i in range(len(a))]
#[[2, 4, 6, 8], [0, 1, 2, 3]]
You can iterate over the lambda functions in the list no matter how many elements stored in it:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
for func in a:
# apply each function on b and turn the results to list:
print(list(map(func, b)))
If you want to store each result:
a = [lambda x:x * 2, lambda x: x-1]
b = [1, 2, 3, 4]
results = []
for func in a:
results.append(list(map(func, b)))
In the special case of a single lambda wrapped in a list, do
result = list(map(a[0], b))
But there are no special cases in python. So for the general case, you can either have a flat list:
result = [f(x) for f in a for x in b]
or a nested one:
result = [[f(x) for f in a] for x in b]
You can reverse the sort order of the lists by swapping the loops over a and b.
You can even go as far as using itertools.product to get the flat case (again with a and b interchangeable to change the order of the result):
result = [f(x) for f, x in itertools.product(a, b)]

Get the integer in between two values

For some coordinate processing I need to be able to find the integers in between 2 values. I know you can use range() for this, but I need it to work even when you switch the 2 values around. range(x, y) won't give the same results as range(y, x). Is there a trick to do this?
EDIT: Torxed's comment was exactly what i needed: range(min(x, y), max(x, y))
There are 3 different options that have been suggested:
As proposed in #Torxed's comment:
range(min(x, y), max(x, y))
As proposed in #tobias_k's comment:
range(*sorted([x, y]))
Or mine, using a ternary condition:
range(x, y) if x < y else range(y, x)
Wasn't quite sure what's asked and just scribbled some weird code.
Both approaches can be useful depending on whether we want to keep the "discarding the 'b'" behaviour.
range(2,5)
#[2, 3, 4]
range(5,2,-1)
#[5, 4, 3]
range(5,2,2 * (5<2) - 1)
#[5, 4, 3]
##### Approach A
a = 5;b = 2
range(a, b, 2*(a<b) -1 )
#[5, 4, 3]
a = 2; b = 5
range(a, b, 2*(a<b) -1 )
#[2, 3, 4]
##### Approach B from #tobias_k
a = 5;b = 2
range(*sorted([a,b]))
#[2, 3, 4]
a = 2;b = 5
range(*sorted([a,b]))
#[2, 3, 4]

Categories

Resources