Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

map with lambda vs map with function - how to pass more than one variable to function?

I wanted to learn about using map in python and a google search brought me to http://www.bogotobogo.com/python/python_fncs_map_filter_reduce.php which I have found helpful.

One of the codes on that page uses a for loop and puts map within that for loop in an interesting way, and the list used within the map function actually takes a list of 2 functions. Here is the code:

def square(x): 
    return (x**2)

def cube(x):
    return (x**3)

funcs = [square, cube]

for r in range(5):
    value = map(lambda x: x(r), funcs)
    print value

output:

[0, 0]
[1, 1]
[4, 8]
[9, 27]
[16, 64]

So, at this point in that tutorial, I thought "well if you can write that code with a function on the fly (lambda), then it could be written using a standard function using def". So I changed the code to this:

def square(x): 
    return (x**2)

def cube(x):
    return (x**3)

def test(x):
    return x(r)

funcs = [square, cube]

for r in range(5):
    value = map(test, funcs)
    print value

I got the same output as the first piece of code, but it bothered me that variable r was taken from the global namespace and that the code is not tight functional programming. And there is where I got tripped up. Here is my code:

def square(x): 
    return (x**2)

def cube(x):
    return (x**3)

def power(x):
    return x(r)

def main():
    funcs = [square, cube]
    for r in range(5):
        value = map(power, funcs)
        print value

if __name__ == "__main__":
    main()

I have played around with this code, but the issue is with passing into the function def power(x). I have tried numerous ways of trying to pass into this function, but lambda has the ability to automatically assign x variable to each iteration of the list funcs.

Is there a way to do this by using a standard def function, or is it not possible and only lambda can be used? Since I am learning python and this is my first language, I am trying to understand what's going on here.

like image 556
Darren Haynes Avatar asked Feb 11 '23 13:02

Darren Haynes


2 Answers

You could nest the power() function in the main() function:

def main():
    def power(x):
        return x(r)

    funcs = [square, cube]
    for r in range(5):
        value = map(power, funcs)
        print value

so that r is now taken from the surrounding scope again, but is not a global. Instead it is a closure variable instead.

However, using a lambda is just another way to inject r from the surrounding scope here and passing it into the power() function:

def power(r, x):
    return x(r)

def main():
    funcs = [square, cube]
    for r in range(5):
        value = map(lambda x: power(r, x), funcs)
        print value

Here r is still a non-local, taken from the parent scope!

You could create the lambda with r being a default value for a second argument:

def power(r, x):
    return x(r)

def main():
    funcs = [square, cube]
    for r in range(5):
        value = map(lambda x, r=r: power(r, x), funcs)
        print value

Now r is passed in as a default value instead, so it was taken as a local. But for the purposes of your map() that doesn't actually make a difference here.

like image 59
Martijn Pieters Avatar answered Feb 13 '23 04:02

Martijn Pieters


Currying is another option. Because a function of two arguments is the same as a function of one argument that returns another function that takes the remaining argument, you can write it like this:

def square(x):
    return (x**2)

def cube(x):
    return (x**3)

def power(r):
    return lambda(x): x(r) # This is where we construct our curried function

def main():
    funcs = [square, cube]
    for y in range(5):
        value = map(power(y), funcs) # Here, we apply the first function
                                     # to get at the second function (which
                                     # was constructed with the lambda above).
        print value

if __name__ == "__main__":
    main()

To make the relation a little more explicit, a function of the type (a, b) -> c (a function that takes an argument of type a and an argument of type b and returns a value of type c) is equivalent to a function of type a -> (b -> c).

Extra stuff about the equivalence

If you want to get a little deeper into the math behind this equivalence, you can see this relationship using a bit of algebra. Viewing these types as algebraic data types, we can translate any function a -> b to ba and any pair (a, b) to a * b. Sometimes function types are called "exponentials" and pair types are called "product types" because of this connection. From here, we can see that

c(a * b) = (cb)a

and so,

(a, b) -> c  ~=  a -> (b -> c)
like image 28
David Young Avatar answered Feb 13 '23 03:02

David Young