How to create dispatch style lambda function using Python? - python

If I have the given dispatch-style function to represent a pair, how can I implement the same using a one-line lambda function?
# ADT Level 0 (dispatch-style pair)
def make_pair(x, y):
"""Dispatch-style pair"""
def dispatch(m):
if m == 0:
return x
elif m == 1:
return y
return dispatch

You could do something like:
make_pair = lambda x,y: lambda m: x if m == 0 else y if m == 1 else None
The outer lambda returns an (inner) lambda waiting for an argument m that returns the bound variables in the scope created by the outer.
>>>one_two = make_pair(1, 2)
>>>one_two(1)
2
>>> one_two(2)
>>>

You can use imbricated lambdas to return a lambda expression:
>>> make_pair=lambda x,y:lambda m:x if m==0 else y
>>> a=make_pair(2,4)
>>> a(1)
4
>>> a(0)
2
lambda m:x if m==0 else y is (more or less) equivalent to your dispatch function (a(2) will return 4 in my case)

Related

Simple Python Issue with Iteration and Lambda

lst = [1, 2, 3]
i = 0
f = lambda x: x * lst[i]
i = 1
print(f(10))
f = lambda x: x * lst[i]
i = 2
print(f(10))
f = lambda x: x * lst[i]
Above is my Python code and I thought it would print out 10, 20, but it says 20, 30. I don't understand why f is modified by i regardless of the explicit assignment. I've got to make it print 10, 20 using an iteration(actually the code is a simplified form of the original one), so it seems that f = lambda x: x * 1 is not allowed.
I think you're expecting f = lambda x: x * lst[i] to store value of i, but it doesn't work that way. When a function is defined, it is just stored to be used later, it is not evaluated when it is defined. It is evaluated only when it is called.
So, when you call f(10) for the first time, you're passing value of x as 10 and the interpreter looks up for the value of i in memory, which is 1 during first function call and 2 during second function call. That's why you get 20 30 as output.
Feel free to ask any question if you still have doubts.
i is global variable. so when you call f it uses the current value of i
look at
f = lambda x: x * lst[i]
lst = [1, 2, 3]
for i in range(len(lst)):
print(f(10))
output
10
20
30
Note, not related to your question, but f = lambda x: x * lst[i] is against PEP8 recommendations:
Always use a def statement instead of an assignment statement that
binds a lambda expression directly to an identifier:
Correct: def f(x): return 2*x
Wrong: f = lambda x: 2*x
The first form means that the name of the resulting function object is
specifically 'f' instead of the generic ''. This is more
useful for tracebacks and string representations in general. The use
of the assignment statement eliminates the sole benefit a lambda
expression can offer over an explicit def statement (i.e. that it can
be embedded inside a larger expression)
Imagine lambda expression as a truly function, so you will get these code:
lst = [1, 2, 3]
i = 0
def f(x):
return x * lst[i]
i = 1
print(f(10)) # currently x=10 and i=1
def f(x):
return x * lst[i]
i = 2
print(f(10)) # currently x=10 and i=2
def f(x):
return x * lst[i]
I know what you mean. In your head, while you define lambda x: x * lst[i], you think number i will be frozen at this time --- just like assignment. When it be called later, the number i will always be the first value. It's not True. The value of i will be referenced when you call this function.

Can I remove double evaluation whilst keeping lambda expression

def bar(x):
# some expensive calculation
<snip>
foo = lambda(x): bar(x) if bar(x) > 10 else 0
However here I have calculated foo twice. Is there a way to still write this as a one liner but avoid the double evaluation. I tried
foo = lambda(x): v if (v = bar(x)) > 10 else 0
but that doesn't work.
Preevaluate:
foo = lambda(x): x if x > 10 else 0
result = foo(bar(x))
Here you can see what is the similarities to your code:
lambda (x): foo(x) if foo(x) > 10 else 0 == (lambda(x): x if x > 10 else 0)(foo(x))
What you seek is not possible unless you create some kind of mutable state object or some other weird trick.
#Alakazam answer is tricky and smart, use it on your own risk. It can be better using iterators and next to avoid extra intermediate list:
lambda x: next(res if res > 10 else 0 for res in (bar(x), ))
Here you have the live example
Yes, you can avoid double evaluation in a lambda. But it's really ugly, and you should avoid it. Here's how it looks:
foo = lambda x: next(b if b > 10 else 0 for b in [bar(x)])
Is this easy to read and understand? No, absolutely not. I'm not going to explain it; see if you can figure it out. This clearly isn't a good solution, so what should you do instead? You should use a real function instead of a lambda.
def foo(x):
b = bar(x)
return b if b > 10 else 0
This is much easier to read and clearly better.
You may want to do this only if your function is very long to execute, else you shouldn't mind running it twice. You can't use list comprehension.
foo = lambda x: [res if res>10 else 0 for res in [bar(x)]][0]
Might as well just define another function:
def bar(x):
# some expensive calculation
<snip>
def bar_threshold(x,t):
y = bar(x)
return y if y>t else 0
foo = lambda x: bar_threshold(x,10)
(or redefine bar, if you find yourself using it only with the threshold)
I've tried by make some function decorator / wrapper...
def funcdec(func):
def inner(x):
if func(x) > 10:
return func(x)
else:
return 0
then
#funcdec
def bar(x):
return x * 2
then i try....:
foo = lambda x: bar(x)
give me result :
foo(2)
return 0
and
foo(10)
return 20

How to read or interpret Lambda in Lambda

I am trying to understand nested lambdas:
f = lambda x, y: y(y(x))
g = lambda x : lambda y: x(y)
print( f(lambda x: x+1, g) (4) )
I was told that this code printed "5". How is this explained, and how should one parse the (4) in the last line?
From my understanding of lambda, if,
h = lambda a, b : a+b
i know that print(h(1,2)) will give 3
as a = 1, b =2, and proceed with a+b = 1+2 =3
f(lambda x: x+1, g) ultimately returns another function. That function is then called with 4 as its argument to produce the final result of 5.
Let h = lambda x: x + 1, because this becomes a mess to trace otherwise.
First, we apply f to h and g.
f(h, g)(4) == (lambda x,y: y(y(x))(h, g)(4)
== g(g(h))(4)
Next, we'll evaluate the inner call to g:
g(g(h))(4) == g((lambda x: lambda y: x(y))(h))(4)
== g(lambda y: h(y))(4)
== g(h)(4)
The last step is an example of eta reduction, to use a term from lambda calculus: a function that applies a second function to an argument is equivalent to the second function itself.
Finally, we evaluate g(h) again the same way, which finally gets us to an expression that doesn't involve passing a function as an argument, and lets us get a final answer.
g(h)(4) == (lambda y: h(y))(4)
== h(4)
== (lambda x: x + 1)(4)
== 4 + 1
== 5
Let's have a go at expanding the logic. First, I'm going to rename some argument names to differentiate your two functions:
f = lambda i, j: j(j(i))
g = lambda x: lambda y: x(y)
Now f(lambda x: x+1, g) is equivalent to:
h = (lambda i, j: j(j(i)))(lambda x: x+1, g)
Here, a function being used as an argument. This is fine, as functions are first-class objects in Python and can be passed around in this way. So evaluating this:
h = g(g(lambda x: x+1))
But g is nothing fancy, it simply takes a function and applies it to an argument. It can be considered an "identity" function with a function as an argument. You can get rid of g altogether. So we have:
h = (lambda x: x+1)
In other words, h just adds one to any input.
h = lambda x: x+1 is a function that returns 1 more than the value passed to it. It is equivalent to:
def h(x):
return x+1
f = lambda x, y: y(y(x)) is a function which takes a value and a function as a pair of arguments and evaluates function(function(value)).
It is equivalent to:
def f(x, y):
return y(y(x))
g = lambda x: lambda y: x(y) is a decorator function that returns a new function based on function passed to it.It is equivalent to:
def g(x):
def new_func(y):
return x(y)
return new_func
the given lambda expressions,
f = lambda x, y: y(y(x))
g = lambda x : lambda y: x(y)
expression to evaluate
f(lambda x: x+1, g) (4)
this reduces to
=> g(g(lambda x: x+1)) (4)
now note that g(g(lambda x: x+1)) returns a function g(lambda x: x+1)
=> g(lambda x: x+1) (4)
now here g(lambda x: x+1) again returns a function (x+1)
=> (x+1) (4)
this evaluates to 4+1 i.e 5
=> 5

Pass a variable that increases as it is called?

How can I do this in python?
a = 0
func(a+=1) # a = 1
func(a+=1) # a = 2
Now I have had to solve it like this:
a = 0
a+=1
func(a)
a+=1
func(a)
...
and there must be a better way, right?
Edit:
Actually I also want to be able to pass it to different functions:
a = 0
a+=1
func1(a)
a+=1
func2(a)
a+=1
func1(a)
...
your solution is okay, but if you want to have a counting side effect, use itertools.count object:
import itertools
def f(x):
print(x)
c = itertools.count()
f(next(c))
f(next(c))
or the variant, performing the next call in the functions themselves (itertools.counter is mutable so you can modify it inside the function):
import itertools
def f(c):
x = next(c)
print(x)
c = itertools.count()
f(c)
f(c)
you can initialize it to a non-zero value: c = itertools.count(3)
Something simple like this:
a = 0
a+=1;func1(a)
a+=1;func2(a)
Each line is only 2 characters longer than your original request.
You can have a wrapping function that calls your function f and simultaneously increment the value of j!
>>> def f(a):print("Value of a is: %d"%a)
...
>>> c=lambda i:(i+1,f(i+1))
>>> j=0
>>> j,_=c(j)
Value of a is: 1
>>> j
1
>>> j,_=c(j)
Value of a is: 2
>>> j
2
There is no way in the sense that in Python, assignment is an instruction, not an operator whose operation returns a value.
You'll have to define a function and either use the global keyword with a or turn a into a mutable. You can also use a decorator for your functions.
a = [0]
#a = 0 # alternative version
def inc(x):
x[0] += 1
return x[0]
# global a # alternative version
# a += 1
# return a
def f1(x):
return x + 1
def f2(x):
return x + 2
# or (inspired by #jpp comment) decorate your functions
def inc1(x):
def inner(x):
x[0] += 1
return x[0]
return inner
#inc1
def f3(x):
return x
for i in range(10):
print(inc(a))
print()
print(f1(inc(a)))
print(f2(inc(a)))
print()
a = [0]
print(f3(a))
print(f3(a))

How to copy a function and set a default parameter in Python?

Let's say I have the following definition:
def foo(x, y):
return x + y
And then I want to have a copy of the function foo (let's call it bar), where x is always equal to 0.
i.e. bar(4) would return 4.
How can I do this in Python?
Thanks!
Maybe like this:
bar = lambda y: foo (0, y)
If you want to delete foo, as user pointed out, you can use:
def foo (x, y): return x + y
bar = (lambda f: lambda y: f (0, y) ) (foo)
print (bar (4) )
del foo
print (bar (4) )
functools.partial is an efficient way to do this:
import functools
bar = functools.partial(foo, 0)
bar(4) == 4
It also lets you supply a function that isn't bound to a variable, or rebind the variable after creating the partial object, which can prevent errors with loops or comprehensions:
from operator import add, sub, mul
funcs = [lambda x: foo(3, x) for foo in add, sub, mul]
funcs[0](3) == 9 # Not 6!
funcs = [functools.partial(foo, 3) for foo in add, sub, mul]
funcs[0](3) == 6 # Works!

Categories

Resources