This article is going to try to shed some light on how to convert map, reduce and filter (often used with lambda expressions) in python into a syntax conforming to „the python way“. At the bottom you will find a download link to execute this stuff for yourself.
l = range(10)
print l
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
This is going to be the list (l) we’re working on.
What we’ll be looking at first, is iterating through this list and applying a function. And what simpler function could there be, than the identity?
a = map(lambda x: x, l)
b = list(x for x in l)
print a, b
#[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
If you don’t really know what the we did in (a) you may want to read up on lambda expressions, a topic I am not covering now. We need the list-method in (b) because map() always returns a list and the for-expression coughs up a generator object which we have to fully evaluate and convert to a list, to get the equivalent. Generator objects I will cover in the future. For now, suffice to say, it has a reason we are applying the list-function.
Next, we are doing an actual calculation by looking for the cubes of our list (l)
a = map(lambda x: x**2, l)
b = list(x**2 for x in l)
print a, b
#[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
Of course you can use actual functions like the cube root or any user defined function.
def usr(p):
return p+p
a = map(lambda x: usr(x), l)
b = list(usr(x) for x in l)
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
We could also combine various functions to create a new anonymous function to apply to our list
a = map(lambda x: usr(x)+5, l)
b = list(usr(x)+5 for x in l)
#[5, 7, 9, 11, 13, 15, 17, 19, 21, 23]
I think you got the idea. Remember, (a) and (b) are equivalent.
Next we will use two inputs. Let’s get ourselves another list then, shall we?
m = range(10,20)
#[10, 11, 12, 13, 14, 15, 16, 17, 18, 19]
And we add both lists together
a = map(lambda x,y: x+y, l, m)
b = list(x+y for x,y in zip(l, m))
print a,b
#[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
Why internally those constructs do something very different, I will cover in the article about generators. But the resulting list is completely the same. Zip() takes lists/iterables as input and convertes them to a list of tuples (n-tuples for n = [number of input iterables]). This tuple is taken apart and put into x and y.
And we can also look at other methods like filter
a = filter(lambda x: x%2==0, l)
b = list(x for x in l if x%2==0)
#[0, 2, 4, 6, 8]
filter() can only filter through input. If you want to apply apply a function, you will have to use map again. In (b) you can do that intuitively:
a = map(lambda x: x*2, filter(lambda x: x%2==0, l))
b = list(x*2 for x in l if x%2==0)
print a,b
#[0, 4, 8, 12, 16]
As we can see, the for-construct can apply map() and filter()
reduce, which applies a two-parameter-function accumulatively throughout an iterable, on the other hand, is looks a bit different on converting (we try to find out the sum of l):
a = reduce(lambda x,y: x+y, l, 0)
b = 0
for x in l:
b = b+x
#45
If we use for in this way, we also can use the filter-if and the map, but we can not use it in the same line as the for-construct. But it is rather trivial, where to use it.
a = reduce(lambda x,y: x+y, map(lambda x: x*2, filter(lambda x: x%2==0, l)), 0)
b = 0
for x in l:
if (x%2==0):
b = b+ x*2
#40
In some cases map, filter, and reduce, (together with lambda expressions) probably are syntactic sugar. But in most cases, the python way looks much more readable.
As a note: you can use map, filter and reduce without lambda, as long as you have a function that has as many parameters as you have input iterables. This is valid and does the same as the one far above:
a = map(usr, l)
#[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
And if you do not want to think about lambdas you can easily create named functions with this simple rule:
lambda arguments: expression
#is the same as
def name(arguments):
return expression
As a final note: The for-construct actually is a generator expression which are handled a bit differently from lists or other „normal“ iterables. This can have impact on execution speed (or control flow in general) but that will be covered later.
Thanks for reading.
Attachment: lambda_anon.txt which I couldn’t name .py because WordPress is paranoid.