How lambda works with decrement
I tried to understand how it reduce
works through this site. The example they mentioned is pretty good and easy to understand.
http://book.pythontips.com/en/latest/map_filter.html#reduce
a = reduce((lambda x, y: x * y), [1, 2, 3, 4])
The above function will be a multiple of each quantity in the list and assign a
.
However, I stopped completely when I stumbled upon the following function in the project.
def compose(*fns):
return reduce(lambda acc, fn: lambda *args: acc(fn(*args)), fns, lambda _: _)
can someone help me to break down this function to understand what it should do
source to share
lambda
you can hardly give in to expressions, and even more so with this, because it returns a new function, also defined with lambda
.
Here's the same expression, but with some different line spacing:
def compose(*fns):
return reduce(lambda acc, fn: lambda *args: acc(fn(*args)),
fns,
lambda _: _)
Now I'll expand on this further by introducing lambda
, passed reduce
in as regular operators def
:
def compose_2_fns(f, g):
# take 2 functions and return a new function that calls the first with the
# result of calling the second
def composed(*args):
return f(g(*args))
return composed
def _initial(x):
return x
def compose(*fns):
return reduce(compose_2_fns, fns, _initial)
Recall what reduce
works by providing it with a method that takes 2 arguments, a sequence of objects (in this case a sequence of functions), and an optional initial value.
reduce(reduce_fn, objs, first_obj)
If no initial value is specified, the abbreviation will take the first object in the sequence, as if you named it like this:
reduce(reduce_fn, objs[1:], objs[0])
Then the decrease function is called like this:
accumulator = first_obj
for obj in objs:
accumulator = reduce_fn(accumulator, obj)
return accumulator
So, what your posted operator reduce
does is create a large function by combining several smaller ones.
functions = (add_1, mult_5, add_3) resulting_function -> lambda *args: add_1(mult_5(add_3(*args)))
So to:
resulting_function(2) -> (((2 + 3) * 5) + 1) -> 26
source to share
What reduce
is just a function composer. It takes an iteration of functions and combines them.
One important point reduce
is that when passing 2 arguments to your function (in this case acc
, fn
) depending on how you use them, python iterates first uses the last two arguments and in subsequent iterations it uses the result of the last calculation instead of the second argument passed to lamda
or any function passed as a constructor in reduce
(in this case fn
). Now, for a better demonstration, here are all the arguments passed to reduce
:
function: lambda acc, fn: lambda *args: acc(fn(*args))
- using the second function can pass arguments to the inner function on each iteration.
iterative argument: fns
initial argument: lambda _: _
If present, it is placed before the elements of the sequence in the calculation and is used by default when the sequence is empty.
As you can see in the passed function, it calls the inner functions with arguments and passes it to the next function, for example fog
in math, which is f(g(x))
. But since you can pass undefined number of arguments to reduce this function, you can write multiple functions.
Here's an example:
In [10]: compose(max, min)([[2, 4], [3, 5]])
Out[10]: 4
Also note that this is not a pythonic and recommended way of composing functions at all. Because, first of all, it is not readable and easy to understand, and secondly, it uses many additional function calls that are not optimal for such a task.
source to share
Not very readable at first sight. Let it decompose:
First, it def compose(*fns):
means that the link function will receive an unknown number of arguments.
Next, we expand the decrease function:
reduce(
lambda acc, fn: lambda *args: acc(fn(*args)),
fns,
lambda _: _
)
As the doc shows , it reduce
takes 3 arguments:
def reduce(function, iterable, initializer=None):
So in your case: function
- lambda acc, fn: lambda *args: acc(fn(*args))
, fns
- this iterable
, and it will initialize withlambda _: _
initializer
indicates that the arguments compose
will be functions. lambda _: _
is a "neutral" for a function (just like "0" for addition or "1" for multiplication). I assume this exists when fns is empty.
Now for the main part:
lambda acc, fn: lambda *args: acc(fn(*args))
it is a function that takes two functions acc
and fn
and returns a lambda function lambda *args: acc(fn(*args))
.
Let's take an example:
>>> reduce((lambda acc, fn: acc ** fn), [1, 2, 3, 4])
1
>>> reduce((lambda acc, fn: fn ** acc ), [1, 2, 3, 4])
262144
Here acc and fn are not functions, but integers. acc
is the "accumulated / reduced" so far, but fn
is the "next" step.
With functions it will be the same as acc
"callable functions" and fn the next function.
So lambda acc, fn: lambda *args: acc(fn(*args))
will return a function (lambda) that will return acc(fn(the_arguments))
.
reduce(lambda acc, fn: lambda *args: acc(fn(*args)), fns, lambda _: _)
will then return a function consisting of applying each function from fns
to its arguments, with a default identifier ( lambda _: _
).
Let's take an example:
>>> def square(x):
... return x**2
...
>>> def increment(x):
... return x+1
...
>>> def half(x):
... return x/2
...
>>> compose(square, increment, half)
<function <lambda> at 0x7f5321e13de8>
>>> g=compose(square, increment, half)
>>> g(5)
9
So, g(x) = square(increment(half(x)))
With Kasramvd example :
compose(max, min)([[2, 4], [3, 5]])
matches:
max(min([[2, 4], [3, 5]]))
min([[2, 4], [3, 5]])
will return [2,4]
and max([2,4])
equal to 4. Socompose(max, min)([[2, 4], [3, 5]])=4
source to share