Python: Understanding unfamiliar function syntax (from UC Berkeley CS61A) - python

I can't do most parts of this question without peeking at the answer key, so I figured I could ask it here. Frankly, I've always had a problem understanding functions with no parameters, and whenever I see one, I think, "OH! they must mean "print" function!" When I'm actually overcomplicating things.
As for Exercise 5, I'm not understanding how to even evaluate the expressions. I feel like it's a simple question, but I can't compute an answer to any of them.
Exercise 4: A Fistful of Functions
For each of the following expressions, what must f be in order for the evaluation of the expression to succeed, without causing an error? Give a definition of f for each expression such that evaluating the expression will not cause an error.
f
f()
f(3)
f()()
f()(3)()
Solution
f = 3
f = lambda: 3
f = lambda x: x
f = lambda: lambda: 3
f = lambda: lambda x: lambda: x
Exercise 5: For A Few Lambdas More
Find the value of the following three expressions, using the given values of t and s.
t = lambda f: lambda x: f(f(f(x)))
s = lambda x: x + 1
t(s)(0) # 1
t(t(s))(0) # 2
t(t)(s)(0) # 3
Solution
1.) 3
2.) 9
3.) 27

For Exercise 4, you are given the following questions and answers.
Q: f
A: f = 3
This is essentially variable assignment.
Q: f()
A: f = lambda: 3
This is similar to a function that takes in no arguments and returns the value 3.
Q: f(3)
A: f = lambda x: x
This is similar to a function that takes in the argument x and returns the value x. So you are given the value 3 and a value of 3 is returned.
Q: f()()
A: f = lambda: lambda: 3
This is similar to a function that takes in no arguments and returns another function that again takes no arguments which will return the value 3.
Q: f()(3)()
A: f = lambda: lambda x: lambda: x
Like the previous example, this will return 3. You have function that takes in no arguments but returns another function that takes in the value x and returns another function that takes in no arguments but returns the value x.
By following the examples above, you can solve Exercise 5 in a similar fashion.
Additionally, Exercise 5 has been explained pretty well in the comments by ChrisP.

Related

How to commute two operators in python?

Let's say I have operator A and operator B which act on f(x) = 2x for example. How do I create a function that says whether A(B(f(x))) == B(A(f(x))) in python?
Here is my attempt:
#Commutation: Creating a function that checks whether operators A and B commutate on some dummy function f(x) = 2*x
#[A,B](2x) =?= 0
def commutate(A,B):
f = lambda x: 2*x
if lambda x: A(B(f)) - B(A(f)) == 0:
print('A and B commute!')
else:
print('A and B do not commute!')
A = lambda x: f(x)**2
B = lambda x: np.log(f(x))
commutate(A,B)
The issue is that I'm always getting that the operators A and B commute, even when giving Operators that don't. I suspect this might have to do with the fact that I can't Define operators like that in python?
In general, it is not possible, based on Rice theorem. Because you desire to check a non-trivial property for two pieces of code, i.e., the property is commutativeness and piece of code here means functions. Hence, it proves that deciding that "any given f and g functions are commutative" is undecidable. Therefore, you can't write such a function to check the commutativeness for two functions.
In your code you have the line
if lambda x: A(B(f)) - B(A(f)) == 0:
In this line you are generating a lambda that is calling the operators on the address of f rather than a result of calling f. Also, you are not calling the lambda that you define in the if statement, so you are checking if the address of the lambda function is True/False which is not what you are looking for.
Lambdas are just functions, but you could test if the operators are commutative on a specific value:
def commutate(A, B, value):
f = lambda x: 2 * x
if A(B(f(value))) == B(A(f(value))):
print(f"A and B commute on value {value}!")
else:
print(f"A and B do not commute on value {value}!")

Defining Anonymous functions by list Comprehension [duplicate]

This question already has answers here:
Local variables in nested functions
(4 answers)
Closed 4 years ago.
I am in need of creating functions (a lot) and storing inside an array or list. I tried using list comprehensions for generating functions (like the code shown below, indeed a simple case).
f = [lambda x: x*i for i in range(0,3)]
which gives
f[0](1) = 2 # expected 0
f[1](1) = 2 # expected 1
f[2](1) = 2 # expected 2 (no prblm with last case)
But instead of using iterators (like list comprehensions) to generate functions, if I write functions explicitly inside a list(defining each function), it works fine.
So, is there anything wrong with the above code(which uses iterators to generate anonymous functions)?
I believe this variant of the syntax will give you what you want:
f = [lambda x, i=n: x * i for n in range(0, 3)]
EXAMPLES
>>> f[0](1)
0
>>> f[1](1)
1
>>> f[2](1)
2
>>>
I believe what #user2357112 is leading you towards is the variation:
from functools import partial
f = [partial(lambda x, i: x * i, i=n) for n in range(0, 3)]
which may not be easy to tease out of the cited duplication page.
The inside of your lambda function is evaluated when you call the function, not when you create the function. At this point point the value of i is 2. You can verify this by running:
>>> f = [lambda x: x*i for i in range(0,3)]
>>> i
2
As cdlane pointed out in his answer, you can fix this by storing the values from the range() statement into a default parameter on your lambda function:
>>> f = [lambda x, i=i: x*i for i in range(0,3)]
>>> f[0](1)
0
>>> f[1](1)
1
>>> f[2](1)
2
This works because unlike the function body, the function definition is evaluated when you create the function.

lambda function of another function but force fixed argument

I just switched to Python from Matlab, and I want to use lambda function to map function f1(x,y) with multiple arguments to one argument function f2(x) for optimization.
I want that when I map the function f2(x) <- f1(x,y=y1) then y will stay constant no matter what y1 changes, in Matlab this is true by default but if I try in Python, it keeps changing as the following examples
>>> def f1(x,y):
>>> return (x+y)
>>> y1 = 2
>>> f2 = lambda x: f1(x,y1)
>>> f2(1)
3
I expect f2(1) stays 3 even if I change y1, however if I change y1, the whole f1(1) also changes as follows
>>> y1 = 5
>>> f2(1)
6
I wonder is there a way that when I declare f2 = lambda x: f1(x,y1) then f1 will take the value of y1 at that time and fix it to f2. The reason for this because I want to dynamically create different functions for different scenarios then sum them all.
I'm still new to Python, please help, much appreciate.
Try:
f2 = lambda x, y=y1: f1(x,y)
Your issue has to do with how closures work in Python
Your version of the lambda function will use the current version of y1. You need to capture the value of y1 on the line where you've defined the lambda function. To do that, you can define it as the default value of a parameter (the y=y1 part).
As already pointed out, your issue comes down to how closures work. However, you really shouldn't be using a lambda for this - lambdas are for anonymous functions. Make a higher-order function with def statements instead:
>>> def f1(x,y):
... return x + y
...
>>> def f1_factory(y):
... def f1_y(x):
... return f1(x,y)
... return f1_y
...
>>> f1_factory(6)(4)
10
>>> f1_factory(5)(4)
9
It also avoids the problem you encountered:
>>> y = 3
>>> newfunc = f1_factory(y)
>>> newfunc(1)
4
>>> y = 20
>>> newfunc(1)
4
>>>
From PEP8:
Always use a def statement instead of an assignment statement that
binds a lambda expression directly to an identifier.
Yes:
def f(x): return 2*x
No:
f = lambda x: 2*x
The first form means that the name of the resulting
function object is specifically 'f' instead of the generic <lambda>.
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)

if and else in python lambda expression

Please help me to understand how this works. Output is 4
a=4
b=7
x=lambda: a if 1 else b
lambda x: 'big' if x > 100 else 'small'
print(x())
First, let's remove this line as it doesn't do anything:
lambda x: 'big' if x > 100 else 'small'
This lambda expression is defined but never called. The fact that it's argument is also called x has nothing to do with the rest of the code.
Let's look at what remains:
a = 4
b = 7
x = lambda: a if 1 else b
print(x())
Here x becomes a function as it contains code. The lambda form can only contain expressions, not statements, so it has to use the expression form of if which is backward looking:
true-result if condition else false-result
In this case the condition is 1, which is always true, so the result of the function x() is always the value of a, assigned to 4 earlier in the code. Effectively, x() acts like:
def x():
return a
Understanding the differences between expressions and statements is key to understanding code like this.
Your x is always equals to 4, as it takes no arguments and if 1 is always True.
Then you have lambda expression that's not assigned to any variable, neither used elsewhere.
Eventualy, you print out x, which is always 4 as I said above.
P.S. I strongly suggest you to read Using lambda Functions from Dive into Python
Let me translate that for you.
You assign to x a lambda function with no arguments. Because 1 always evaluates as true, you always return the externally defined variable a, which evaluates as 4.
Then, you create a lambda function with one argument x, which you don't assign to a variable/access name, so it is lost forever.
Then, you call function x, which always returns a. Output is 4.

recursive lambda-expressions possible?

I'm trying to write a lambda-expression that calls itself, but i can't seem to find any syntax for that, or even if it's possible.
Essentially what I wanted to transfer the following function into the following lambda expression: (I realize it's a silly application, it just adds, but I'm exploring what I can do with lambda-expressions in python)
def add(a, b):
if a <= 0:
return b
else:
return 1 + add(a - 1, b)
add = lambda a, b: [1 + add(a-1, b), b][a <= 0]
but calling the lambda form of add results in a runtime error because the maximum recursion depth is reached. Is it even possible to do this in python? Or am I just making some stupid mistake? Oh, I'm using python3.0, but I don't think that should matter?
Maybe you need a Y combinator?
Edit - make that a Z combinator (I hadn't realized that Y combinators are more for call-by-name)
Using the definition of the Z combinator from Wikipedia
>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
Using this, you can then define add as a completely anonymous function (ie. no reference to its name in its definition)
>>> add = Z(lambda f: lambda a, b: b if a <= 0 else 1 + f(a - 1, b))
>>> add(1, 1)
2
>>> add(1, 5)
6
Perhaps you should try the Z combinator, where this example is from:
>>> Z = lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
>>> fact = lambda f: lambda x: 1 if x == 0 else x * f(x-1)
>>> Z(fact)(5)
120
First of all recursive lambda expressions are completely unnecessary. As you yourself point out, for the lambda expression to call itself, it needs to have a name. But lambda expressions is nothing else than anonymous functions. So if you give the lambda expression a name, it's no longer a lambda expression, but a function.
Hence, using a lambda expression is useless, and will only confuse people. So create it with a def instead.
But yes, as you yourself discovered, lambda expressions can be recursive. Your own example is. It's in fact so fantastically recursive that you exceed the maximum recursion depth. So it's recursive alright. Your problem is that you always call add in the expression, so the recursion never stops. Don't do that. Your expression can be expressed like this instead:
add = lambda a, b: a > 0 and (1 + add(a-1, b)) or b
Which takes care of that problem. However, your first def is the correct way of doing it.
add = lambda a, b: b if a <= 0 else 1 + add(a - 1, b)
You want the Y combinator, or some other fixed point combinator.
Here's an example implementation as a Python lambda expression:
Y = lambda g: (lambda f: g(lambda arg: f(f)(arg))) (lambda f: g(lambda arg: f(f)(arg)))
Use it like so:
factorial = Y(lambda f: (lambda num: num and num * f(num - 1) or 1))
That is, you pass into Y() a single-argument function (or lambda), which receives as its argument a recursive version of itself. So the function doesn't need to know its own name, since it gets a reference to itself instead.
Note that this does get tricky for your add() function because the Y combinator only supports passing a single argument. You can get more arguments by currying -- but I'll leave that as an exercise for the reader. :-)
a little late ... but I just found this gem # http://metapython.blogspot.com/2010/11/recursive-lambda-functions.html
def myself (*args, **kw):
caller_frame = currentframe(1)
code = caller_frame.f_code
return FunctionType(code, caller_frame.f_globals)(*args,**kw)
print "5! = "
print (lambda x:1 if n <= 1 else myself(n-1)*n)(5)

Categories