This is quite n00bish, but I'm trying to learn/understand functional programming in python. The following code:
foos = [1.0,2.0,3.0,4.0,5.0] bars = [1,2,3] def maptest(foo, bar): print foo, bar map(maptest, foos, bars)
1.0 1 2.0 2 3.0 3 4.0 None 5.0 None
Q. Is there a way to use map or any other functional tools in python to produce the following without loops etc.
1.0 [1,2,3] 2.0 [1,2,3] 3.0 [1,2,3] 4.0 [1,2,3] 5.0 [1,2,3]
Just as a side note how would the implementation change if there is a dependency between foo and bar. e.g.
foos = [1.0,2.0,3.0,4.0,5.0] bars = [1,2,3,4,5]
1.0 [2,3,4,5] 2.0 [1,3,4,5] 3.0 [1,2,4,5] ...
P.S: I know how to do it naively using if, loops and/or generators, but I'd like to learn how to achieve the same using functional tools. Is it just a case of adding an if statement to maptest or apply another filter map to bars internally within maptest?
The easiest way would be not to pass
bars through the different functions, but to access it directly from
foos = [1.0,2.0,3.0,4.0,5.0] bars = [1,2,3] def maptest(foo): print foo, bars map(maptest, foos)
With your original
maptest function you could also use a lambda function in
map((lambda foo: maptest(foo, bars)), foos)
Are you familiar with other functional languages? i.e. are you trying to learn how python does functional programming, or are you trying to learn about functional programming and using python as the vehicle?
Also, do you understand list comprehensions?
is directly equivalent (*) to:
[f(x) for x in sequence]
In fact, I think
map() was once slated for removal from python 3.0 as being redundant (that didn't happen).
map(f, sequence1, sequence2)
is mostly equivalent to:
[f(x1, x2) for x1, x2 in zip(sequence1, sequence2)]
(there is a difference in how it handles the case where the sequences are of different length. As you saw,
map() fills in None when one of the sequences runs out, whereas
zip() stops when the shortest sequence stops)
So, to address your specific question, you're trying to produce the result:
foos, bars foos, bars foos, bars # etc.
You could do this by writing a function that takes a single argument and prints it, followed by bars:
def maptest(x): print x, bars map(maptest, foos)
Alternatively, you could create a list that looks like this:
[bars, bars, bars, ] # etc.
and use your original maptest:
def maptest(x, y): print x, y
One way to do this would be to explicitely build the list beforehand:
barses = [bars] * len(foos) map(maptest, foos, barses)
Alternatively, you could pull in the
itertools contains many clever functions that help you do functional-style lazy-evaluation programming in python. In this case, we want
itertools.repeat, which will output its argument indefinitely as you iterate over it. This last fact means that if you do:
map(maptest, foos, itertools.repeat(bars))
you will get endless output, since
map() keeps going as long as one of the arguments is still producing output. However,
itertools.imap is just like
map(), but stops as soon as the shortest iterable stops.
itertools.imap(maptest, foos, itertools.repeat(bars))
Hope this helps :-)
(*) It's a little different in python 3.0. There, map() essentially returns a generator expression.