while > for > foreach - increasingly higher level abstractions.
article stops at iteration (foreach) but misses an even higher level abstraction: filter, map, reduce. each of these can be implemented on top of foreach, and i haven't yet seen a loop that can't be decomposed into a combination of map, filter, reduce. also note that filter/map/reduce are expressions, not statements, so they are more "mathy" and thus easier to reason about.
its interesting that these are not necessarily idiomatic python despite being a slightly higher level abstraction. note they are idiomatic in all "real" functional languages, which python is decidedly not. also list comprehensions are faster than map/filter in python. (this is probably python's fault and is really dumb because you could just implement map/filter as a list comprehension. some people have criticized Guido for some of his design choices[1].)
> its interesting that these are not necessarily idiomatic python despite being a slightly higher level abstraction. note they are idiomatic in all "real" functional languages, which python is decidedly not. also list comprehensions are faster than map/filter in python. (this is probably python's fault and is really dumb because you could just implement map/filter as a list comprehension. some people have criticized Guido for some of his design choices.)
I don't miss map/reduce syntax the slightest. List comprehensions (for when I need to read multiple times a list result) and generators expressions (for lazy evaluation and genericity) are incredibly readable and efficient .
Here's an example script[0] I threw in a cron to report on some daily FTP transfers from a legacy system. Those list comprehensions are very descriptive, like a mathematical set syntax. In comparison e.g Ruby's map/inject feels terribly awkward and alien to me. [1] (and [2], although a bit academic) is an invaluable resource on the subject.
He does do a "map", although without using the function of that name:
answers = [f(x) for x in iterable]
Also, the "zip" operation gives loops that seem to me to be unable to be decomposed into map, filter, reduce. E.g., how would you print a file with line numbers using only map, filter, reduce?
Regardless, I agree with your basic point: that thinking of loops in terms of high-level loop primitives, is a Good Thing.
Well, strictly speaking, you have answered my objection. But I guess I was thinking in Haskell. If Python's map takes multiple iterables, then, yes, zip is a special case. But writing zip by mapping a function without side effects, using a "map" that works only on a single iterable (as in Haskell) strikes me as a rather different thing.
So I suppose that the truth of "this loop can be written using map" depends on exactly what one means by "map".
this works but is sort of icky in python, any ideas? this is also where functional python starts to fall apart - no tail call recursion, and default data structures aren't persistent. based on a haskell solution which is nicer. http://stackoverflow.com/questions/2578930/understanding-thi...
I'm sorry, is this a joke? "straightforward"? Why would you use that code when you could implement the same thing more simply like this?
def line_number_mogrifier(f):
num = 0
for line in f:
yield (num, line)
num += 1
The functional proponents say that eventually everything will be functional, I guess I'll wait and see how that goes over. People think procedurally. Functional constructs may have some technical advantages, but if adopting them shrinks the pool of effective developers, it won't catch on.
Can you control the ordering of map/filter/reduce? As in, I want to iterate through a matrix by iterating through the columns and rows in reverse order? As in:
[9,6,3]
[8,5,2]
[7,4,1]
I've just started using map/filter/reduce so I'm not sure.
No. They start from the beginning and go to the end. Conceptually it could work on any iterable, and not just a list container.
You can, however, use the 'reversed()' function, as in "for row in reversed(rows): for col in reversed(row): print col"
There is a special double-underscore method, which lists and other data structures support, to allow iteration in reverse with the normal performance of forward iteration.
What data structure are you using to represent your matrix? A list of lists? If so, it's not terribly difficult:
>>> a = [[1,2,3], [4,5,6], [7,8,9]]
>>> b = zip(*map(reversed, reversed(a))
>>> b
[(9, 6, 3), (8, 5, 2), (7, 4, 1)]
>>> reduce(operator.add, b) # could also use a lambda with + instead of operator.add...
(9, 6, 3, 8, 5, 2, 7, 4, 1)
Simple and useful overview. I find enumerate() quite useful. I should probably define generators more often.
One built-in function I basically never use is zip(). I know how it's used and what it does, and I grok the names/ages types of examples that are always used to explain it. But I've simply never needed it in real code. Anyone else use it often, and in what contexts?
If you want to transpose a matrix-like list of lists, but the sizes don't all match up, you can use izip_longest from itertools and give it a fillvalue to "fill in" for the missing elements:
I use it rarely in Python, although it's sometimes useful. I use it more often in other languages to replicate Python's enumerate, since zip(range(len(seq)),seq) is basically enumerate(seq) (although with this implementation it's not a generator).
For example, trying to solve this problem (CodeJam 2008 minimum scalar product):
You are given two vectors v1=(x1,x2,...,xn) and v2=(y1,y2,...,yn). The scalar product of these vectors is a single number, calculated as x1y1+x2y2+...+xnyn.
Haskell:
sum(zipWith (*) [1,2,3][3,2,1])
Python is not inherently functional, just supports a few convenience idioms. It has zip, but no zipWith. As a result you have to use list comprehension to perform the same:
First examples of generators that I've seen that made them look useful. I'd never thought of using a generator as a filter, or for turning nested for-loops into one for-loop. I have some code I can go clean up now.
article stops at iteration (foreach) but misses an even higher level abstraction: filter, map, reduce. each of these can be implemented on top of foreach, and i haven't yet seen a loop that can't be decomposed into a combination of map, filter, reduce. also note that filter/map/reduce are expressions, not statements, so they are more "mathy" and thus easier to reason about.
its interesting that these are not necessarily idiomatic python despite being a slightly higher level abstraction. note they are idiomatic in all "real" functional languages, which python is decidedly not. also list comprehensions are faster than map/filter in python. (this is probably python's fault and is really dumb because you could just implement map/filter as a list comprehension. some people have criticized Guido for some of his design choices[1].)
[1] http://www.quora.com/What-are-the-main-weaknesses-of-Python-...
also on topic: http://docs.python.org/library/itertools.html