Spencer Felton
Spencer Felton

Reputation: 21

Passing function objects and using them in other functions in Python 3.8.4

I was looking at this question:

cons(a, b) constructs a pair, and car(pair) and cdr(pair) returns the first and last element of that pair. For example, car(cons(3, 4)) returns 3, and cdr(cons(3, 4)) returns 4.

Given this implementation of cons:

def cons(a, b):
    def pair(f):
        return f(a,b)
    return pair

Implement car and cdr.

I recognised the lambda expression, but I'm still unsure of how it works. The answer is given as:

def car(f):
    z = lambda x, y: x
    return f(z)

def cdr(f):
    z = lambda x, y: y
    return f(z)

If f is the function object, isn't passing z to f calling lambda f : f(a,b), where z has 2 arguments but the other lambda only has 1 argument?

How does this solution work?

Upvotes: 1

Views: 118

Answers (2)

Yam Mesicka
Yam Mesicka

Reputation: 6601

Let's evaluate car(cons(3, 4)).

cons(3, 4)

a = 3
b = 4
def pair(f):
    return f(3, 4)
return pair

So right now we have car(pair), where pair is:

def pair(f):
    return f(3, 4)

car(pair)

Let's see what happens in car:

f = pair
z = lambda x, y: x
return f(z)

So the last statement can be actually read as:

return pair(z)

or

return pair(lambda x, y: y)

Let's rewrite z as a regular function for the sake of simplicity:

def z(x, y):
    return x

return pair(z)

Now we run pair with z as parameter.

Remember, pair is defined as:

def f(n):
    return n(3, 4)

So n = z, and we should return:

z(3, 4)

What z does is actually "return the first parameter".

And... Here you have it :)

Upvotes: 0

chepner
chepner

Reputation: 531948

You have to recognize how a pair is represented. A pair consists of three things: a first element, a second element, and a function. Most importantly, the two elements are not accessible "directly" (though see below): you can only get to them via the third member of the pair. That function takes another function, and returns the result of applying that function to the two "hidden" elements.

car takes the pair function and applies it to a function that returns the first of its two arguments.

cdr takes the pair function and applies it to a function which returns the second of its two arguments.

You can trace this as follows, made easier if you rewrite cons = lambda a, b: lambda f: f(a, b):

car(cons(3, 5)) == cons(3, 5)(lambda x, y: x)
                == (lambda a, b: lambda f: f(a, b))(3, 5)(lambda x, y: x)
                == (lambda f: f(3, 5))(lambda x, y: x)
                == (lambda x, y: x)(3, 5)
                == 3


cdr(cons(3, 5)) == cons(3, 5)(lambda x, y: y)
                == (lambda a, b: lambda f: f(a, b))(3, 5)(lambda x, y: y)
                == (lambda f: f(3, 5))(lambda x, y: y)
                == (lambda x, y: y)(3, 5)
                == 5

cons(3, 5) essentially stores the values 3 and 5 in the __closure__ attribute of the pair function it creates.

>>> p1 = cons(3, 5)
>>> p1.__closure__[0].cell_contents
3
>>> p1.__closure__[1].cell_contents
5

Upvotes: 1

Related Questions