Charming Python : Iterators and simple generators
started 9/27/2001; 12:37:33 PM - last post 10/14/2001; 4:08:07 AM
|
|
Ehud Lamm - Charming Python : Iterators and simple generators
9/27/2001; 12:37:33 PM (reads: 5916, responses: 26)
|
|
Charming Python : Iterators and simple generators |
Python 2.2 introduces a new construct accompanied by a new keyword. The construct is generators; the keyword is yield. Generators make possible several new, powerful, and expressive programming idioms, but are also a little bit hard to get one's mind around at first glance. In this article, David provides a gentle introduction to generators, and also to the related topic of iterators.
Not that these ideas are extremely new: iterators are from CLU (check out our prevous discussions of this landmark language), and gnererators are, among other things, the basis for goal directed evaluation in Icon. Still, this is a nice intro.
Posted to Python by Ehud Lamm on 9/27/01; 12:38:34 PM
|
|
|
|
Noel Welsh - Re: Charming Python : Iterators and simple generators
9/28/2001; 1:15:59 AM (reads: 4488, responses: 0)
|
|
The direction Python is taking is a bit surprising. They don't need generators; they can be constructed from proper lexical closures. There is a paper by Henry Baker that shows how to do coroutines (i.e. generators) using only closures.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
9/28/2001; 6:04:40 AM (reads: 4488, responses: 0)
|
|
Can you post a link to this paper?
Anyway, this seems easy enough to do, only it seems inefficient. Continuations are better for this sort of thing.
|
|
Adewale Oshineye - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:01:28 AM (reads: 4457, responses: 0)
|
|
I've been reading Richard P. Gabriel's Patterns of Software and he has this discussion about the factors that contribute to a language's popularity or obscurity. One of his main points is that languages that demand "mathematical sophistication" tend to fare badly. So a language like Scheme remains out of the mainstream as it requires a programmer to grasp the idea of recursion before they can write useful programs.
One of the things that first got me interested in Python was the sheer readability of the language. Even without understanding the syntax most people I work with can read my Python code. Features like generators and (to a lesser extent) list comprehensions are very off-putting to new Python programmers. They allow a small(?) fraction of the Python community to solve particular problems more easily at the expense of increasing the cognitive load on the rest.
Just ignoring the features you don't like isn't a solution because you end up with people using various sub-sets of the language.
On the other hand I really like map, apply and higher-order functions.
As Python evolves one of the interesting things to watch is how many people actually use the new functional programming features. Javascript provides a clear example of what happens when a language evolves away from it's userbase. Most users don't use closures, higher order functions or function pointers. When people do try out the more outlandish features like eval they're usually mis-used.
This wasn't meant to be a rant but generators just seems to add to the list of Python features I can't easily explain to a co-worker in 5 minutes with a real-world example.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:16:15 AM (reads: 4462, responses: 1)
|
|
One of his main points is that languages that demand "mathematical sophistication" tend to fare badly.
...features I can't easily explain to a co-worker in 5 minutes with a real-world example.
I don't buy this. First of all why is recursion more mathematical than iteration? I found it perfectly natural and easy to understand when I was first exposed to programming, using Logo.
The way I'd rephrase this assertion, so that I can agree with it, is to say that langauge constructs that can only be explained mathematically will pose entry barriers.
Mapping a routine across a list is something very natural from a programming point of view: it is in fact easier to understand than general looping constructs.
The problem is, most languages with this feature have "strange" syntax, and jargon filled tutorials.
Now consider this explanation of generators (from an overview of Icon)
If the result produced by a generator does not lead to the success of an enclosing expression, however, the generator is resumed to produce another value. An example is
if (i := find("or", sentence)) > 5 then write(i)
Here the first result produced by the generator, 3, is assigned to i, but this value is not greater than 5 and the comparison operation fails. At this point, the generator is resumed and produces the second position, 23, which is greater than 5. The comparison operation then succeeds and the value 23 is written.
Seems to me this is quite easy to understand.
As to Python being readable, perhaps it's my previous exposure to other languages but I have yet to see a Python example that I would call readable. I'd put it somewhere near APL on a readability scale...
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:16:59 AM (reads: 4451, responses: 0)
|
|
Have you considered that maybe "features that [you] can't easily explain to a co-worker in 5 minutes" are the most useful ones? The features which are easiest to explain to others are the ones that are equivalent to ones which they are familiar with. If your co-worker is familiar with C, and you every feature of language X is easy for him to grasp, then perhaps there is no point in switching from C.
There's no such thing as a free lunch.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:19:51 AM (reads: 4443, responses: 0)
|
|
How does that saying go? Fortran programmers are bound to program Fortran in every language...
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:29:09 AM (reads: 4460, responses: 1)
|
|
As to Python being readable, perhaps it's my previous exposure to other languages but I
have yet to see a Python example that I would call readable. I'd put it somewhere near APL
on a readability scale...
Wow! I'm no Python fan, but I would not have been so harsh.
I think when most people use the word "readability" they only mean it in the simplest sense possible, namely that you can easily parse a program. But I think this is the least useful notion of readability, and indeed I think it is mostly a matter of acclimation, in the same way that speech in a foreign language gets easier to parse as you hear it more often.
A better definition of readability is that a language is readable when you can easily tell by reading a program that it satisfies its specification, or what sort of specification it is intended to satisfy. By this definition, the more declarative a language is, the more readable it is.
For me, it is very hard to guess from looking at a Python program what it is supposed to do, in a semantic sense. I can easily tell you, for example, what is going on in the store, and what will happen given a particular state of the store. But that does not tell me what the program is trying to achieve, only how it is trying to achieve something. So, in that sense, I don't find Python much more readable than Pascal.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:45:39 AM (reads: 4466, responses: 0)
|
|
[Adewale:] One of his main points is that languages that demand "mathematical sophistication" tend to
fare badly.
[Ehud:]
I don't buy this. First of all why is recursion more mathematical than iteration? I found it
perfectly natural and easy to understand when I was first exposed to programming, using
Logo.
I agree with this.
The reason the world's programmers favor iteration over recursion is because that is what they were taught, and what they see around them every day, and what their comrades use.
The way I'd rephrase this assertion, so that I can agree with it, is to say that langauge
constructs that can only be explained mathematically will pose entry barriers.
Both iteration and recursion have a mathematical semantics, and they can both be explained in non-mathematical terms with, probably, about the same degree of success, but it happens that nearly all of the world's mathematical literature historically favors recursion.
I think there is no such thing as more or less "mathematical sophisticated" languages: there are more or less expressive languages, more or less orthogonal languages, and better- or worse-defined languages.
I think many people regard many intuitively simple features as "mathematically sophisticated" because, when a feature is amenable to mathematical reasoning, then there are a whole bunch of theorems and associated facts which come along with it, and it seems more complex, because we regard those facts as an important part of that feature.
On the other hand, when a feature is complex or hard to reason about, those facts are harder to formalize, and so people only ever write them out informally, or learn them as "folklore".
I think this is the case with recursion and iteration. Recursion is dead simple. Really. But it has far-reaching consequences which are easy to reach, and there is a whole theory of fixpoints, algebras, induction and so on.
But the sort of iteration you see in many imperative languages is quite hard to reason about effectively, since it depends on global state, arithmetic, etc. Consequently, it is easier to make complex and interesting statements about recursion than it is to do the same about for- and while-loops, and so you see a lot more of them. Unfortunately, that seems to constitute too much cognitive baggage for people who want put square pegs in round holes.
Oh well. That's their loss.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
9/28/2001; 9:57:20 AM (reads: 4532, responses: 0)
|
|
I'm no Python fan, but I would not have been so harsh.
Well, I was trying to over state my case, but I am simply sick of hearing that Python is so great, esp. for beginners since it is so readable. Except for the indentation as syntax approach, which I am no great fan of, why is Python supposed to be so readable?
Now being an Ada fan, I can give many examples why Ada claims to be readable. You can argue about these features, but at least it is pretty obvious that they are there...
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
9/28/2001; 10:34:50 AM (reads: 4459, responses: 0)
|
|
I am simply sick of hearing that Python is so
great, esp. for beginners since it is so readable. Except for the indentation as syntax
approach, which I am no great fan of, why is Python supposed to be so readable?
Heh. There are a few legitimate things in Python which improve readability, though. For example, it has rudimentary pattern-matching for tuple assignment, and you can iterate over a list by binding each element to a variable rather than indirectly referring to it by its index.
What I find remarkable about Python programmers is how many of them are convinced that such features are unique to, or originated with, Python. For example, the indentation syntax appeared originally, I think, in Landin's ISWIM, which is, what, 30 years old?, and continues in Miranda, Haskell and doubtless tons of other, admittedly experimental, languages. Practically all functional languages have pattern-matching; even Scheme has several pattern-matching macro packages. And list iteration is available in any semi-functional language like ML, though the syntax is different.
Actually, what bugs me most about advocates of Python and similar languages is how often I have to hear the phrase, "I dunno, it just fits your brain!" You have to wonder about a person who claims that their brain is naturally adapted to processing Python code, or indeed, the source for any programming language... :)
I think these things are nearly all acquired skills. There are a few notions from HCI research which one might legitimately say make humans-computer interaction automatically more intuitive, but they have so very little bearing on programming languages, and are obvious to most people: things like, "you must not make all your reserved keywords longer than 1024 characters." :) Anyway, I have yet to see a real HCI study on Python.
|
|
Fredrik Lundh - Re: Charming Python : Iterators and simple generators
9/28/2001; 1:32:34 PM (reads: 4427, responses: 1)
|
|
"I dunno, it just fits your brain!" You have to wonder about a person who claims that their brain is naturally adapted to processing Python code, or indeed, the source for any programming language...
the "Python fits your brain" quote is by Bruce Eckel,
and has nothing to do with what you're talking about.
(but judging from the rest of this thread, it looks like it
would be a waste of time to try to explain what he really
meant)
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
9/28/2001; 2:22:15 PM (reads: 4519, responses: 0)
|
|
judging from the rest of this thread, it looks like it would be a waste of time to try to explain what he really meant
You may not like the views expressed here, but I think we have quite a track record for civilised and professional discussion.
You may want to give us a try.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
9/29/2001; 2:40:22 AM (reads: 4399, responses: 0)
|
|
the "Python fits your brain" quote is by Bruce Eckel, and has nothing to do with what you're talking about.
It was, of course, an intentionally flippant remark, as evidenced by the smiley.
I assume what is meant is that Python is somehow more intuitive than other languages, in the same way that Mac advocates claim that MacOS is more intuitive than Windows or UNIX. The "Principle of Least Astonishment" and whatnot.
If that's wrong, I would gladly be corrected. If that's right, I would like to hear what makes Python so well-fitted to my brain because, frankly, my brain says otherwise. :)
|
|
Adewale Oshineye - Re: Charming Python : Iterators and simple generators
9/29/2001; 11:20:57 AM (reads: 4398, responses: 0)
|
|
I think that Python is more likely to fit your brain if you have a particular background. Now that Python 2.2 has lexical scopes it has become very easy to learn for someone who has been programming in Java, C, C++ or any other language with lexical scoping, algol68-derived syntax and infix notation. The problem of course for people, like me, with that kind of background is that Python "fits your brain" until you come across the functional programming extensions. Then life gets complicated and you have to concentrate to understand language features because you've never come across anything like them before.
|
|
kaweah - Re: Charming Python : Iterators and simple generators
10/1/2001; 1:57:30 PM (reads: 4335, responses: 0)
|
|
I learned Python and a bit of Perl at around the same time, several years ago. When dealing with Perl, every time I tried to do anything even slightly different, I would have to look it up in the documentation. With Python, every time I guessed "if this works this way, then that should work that way", I was right. With Perl, those kinds of guesses didn't even appear in my mind, despite the fact that I already *knew* awk, sed, and was and am quite comfortable with regular expressions. That's the way that I agree with the "fits my brain" claims. I doubt that any of those claims have said "Frank, Python will fit *your* brain".
I sorta feel similar about Haskell ("fits") vs Ocaml ("does not fit, probably because of the funky syntax"). Haven't got a lot of experience with either of these, though, which is why it's only "I sorta feel".
About recursion vs iteration: I believe that there are still a lot of programmers who think of programming with a very mechanical metaphor: I take this thing, pick it up, put it over there in that bag, throw the bag over to the other end of the net ... etc. This metaphor extends well enough to iteration (I do this thing again and again), but not so well to recursion.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
10/2/2001; 2:52:21 AM (reads: 4310, responses: 1)
|
|
That's the way that I agree with the "fits my brain" claims. I doubt
that any of those claims have said "Frank, Python will fit *your* brain".
OK then, maybe you should change that motto to, "Python: it fits your brain, unless your brain belongs to Frank Atanassow." :)
Interesting that you feel Haskell fits better than Ocaml, though. I would have imagined the opposite.
But anyway, the fact of being comfortable with something says little about its usefulness or its correctness. Years ago, C++ "fitted my brain", but I learned Haskell anyway because I thought it was interesting and powerful. Now Haskell fits my brain, and C++ doesn't. I used to be comfortable with the idea that light was a wave; then my teachers told me what particle-wave duality was, and I was not comfortable with it for a long time (maybe still not?), but I accepted it because it's a better model. To get a better understanding of things, you have to put up with things that you're not comfortable with; that's the way science works. Mathematics also works that way to an extent, and so does programming.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/3/2001; 2:17:26 AM (reads: 4370, responses: 0)
|
|
Years ago, C++ "fitted my brain", but I learned Haskell anyway because I thought it was interesting and powerful. Now Haskell fits my brain, and C++ doesn't.
Indeed. But don't you agree that this may be seen as argument for adding features to the languages themselves, instead of relying on extensions? As in "Scheme will make you comfortable with the general concept of continuations, Icon will provide a simpler more focused tool like generators"? (substitute Prolog, CLU or whatever for Icon) This pertains to learning speed, and ease of use.
Recall the Sapir-Whorf hypothesis: the tools in the language influence, not to say control, the way you think.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
10/3/2001; 3:45:35 AM (reads: 4274, responses: 1)
|
|
But don't you agree that this may be seen as argument for adding features to the
languages themselves, instead of relying on extensions?
No, not at all. I guess you are saying that, "You can translate Haskell to C++, but the fact that Haskell is still superior proves that making certain features primitive is intrinsically valuable." It is true that you can translate Haskell to C++, but that translation does not preserve types and other static guarantees. There is no way for me to get a C++ compiler to respect and check referential transparency, or beta-substitution, or tons of other properties which hold of Haskell programs. But, AFAIK, there is nothing that Icon's generators give you which the encoding using continuations doesn't.
Recall the Sapir-Whorf hypothesis: the tools in the language influence, not to say control,
the way you think.
(For those who don't know, the Sapir-Whorf hypothesis is an old conjecture from the field of linguistics which claims that humans can't conceptualize what they can't express in language, i.e., you can't think what you can't say.)
First of all, the Sapir-Whorf hypothesis has is widely accepted to be wrong. There is lots of counterevidence, for example the fact that when you learn a foreign language, you can learn words which are not inexpressible in your own language.
That said, I think it's certainly true that it's easier to think about things which you have a name for. That's partly why, in mathematics, we try to develop rigorous syntaxes for certain fields: an expression in a formal language is a name for its denotation.
But, if anything, I think the Sapir-Whorf hypothesis is an argument in my favor. After all, if I implement generators using continuations, then I can rigorously explain generators in a language (namely the source language) which is familiar to the programmer already. If generators are primitive, then I have to give some fuzzy explanation which introduces new concepts and terms with which the programmar is unfamiliar.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/3/2001; 4:02:09 AM (reads: 4363, responses: 0)
|
|
No, not at all. I guess you are saying that, "You can translate Haskell to C++, but the fact that Haskell is still superior proves that making certain features primitive is intrinsically valuable."
This wasn't my point. What I tried to say is that experience with Haskell changed the way you thought. I am talking here about the cognitive aspects of using the languages, not their semantics.
But, if anything, I think the Sapir-Whorf hypothesis is an argument in my favor. After all, if I implement generators using continuations, then I can rigorously explain generators in a language (namely the source language) which is familiar to the programmer already. If generators are primitive, then I have to give some fuzzy explanation which introduces new concepts and terms with which the programmar is unfamiliar.
I am not fighting you on this... I think there is room for Scheme and for Icon.
All I tried to explain is that there are situations in which using a construct is more important than understanding how it can be implemented with continuations. More so: some people can use generators is exposed to them, but will never think of extending a language.
Notice that I am not saying the Perl or Ruby are better the Scheme for scripting. I am far from sure that this is the case. I am just trying to explain why I think these languages came to exist - and I don't think the whole story is lazyness or lack of education. These factos are important, but there are some others as well...
|
|
Bart Meerdink - Re: Charming Python : Iterators and simple generators
10/4/2001; 4:49:16 PM (reads: 4314, responses: 0)
|
|
First of all why is recursion more mathematical than iteration?
Schemers are a bit like the French (or Americans) thinking foreigners are stupid because they cannot express themselves fluently (in their language, that is). Even "simple" recursive functions can be hard to grasp. Compare Ackermann's function:
A(0, n) = n + 1
A(m+1, 0) = A(m, 1)
A(m+1, n+1) = A(m, A(m+1, n))
(E.L. quotes explanation about generators in Icon:)
if (i := find("or", sentence)) > 5 then write(i)
Seems to me this is quite easy to understand.
Really, to me anyway, this syntax seems quite misleading.
What appears on the surface to be a simple imperative statement containing a function "find" turns out to be something else altogether. And what happens when there are two or more generators in one expression? Like:
if (i := find("A", "AA")) != find("B", "BB") then write(i)
As to Python being readable, perhaps it's my previous exposure to other languages but I have yet to see a Python example that I would call readable. I'd put it somewhere near APL on a readability scale...
Genuine flaimbait, I would say.
I think that, for example, allowing indentation to be misleading as to the meaning of a program is criminal. Therefore Python is quite correct in attaching meaning to indentation, rather than requiring spurious delimiters.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/5/2001; 8:29:24 AM (reads: 4213, responses: 0)
|
|
Your example is bogus since Ackermann's function is complictaed to understand not because of recursion but beacuse of what it does. A better example is iterative vs. recursive code for doing something interesting like sorting or merging. Recursion most often wins.
But what makes you comment esp. funny is that you claim I am a Schemer. I am honred, but by no stretch of the imagination am I (yet) a Schemer. Of the languages I know Scheme is one of the more recent.
Recursion was easy when I first "discovered" it for myself when I learned Logo in school when I was 10.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
10/11/2001; 4:52:10 AM (reads: 4130, responses: 4)
|
|
All I tried to explain is that there are situations in which using a construct is more important
than understanding how it can be implemented with continuations. More so: some people can
use generators is exposed to them, but will never think of extending a language.
Sure, but what I am saying is that you don't need to know how to implement generators in Scheme to use them: you just import them from some library. And then if you want do-while loops or something you can import those. You don't have to understand the implementation via continuations or whatever any more than you have to understand the implementation via C or Java of Icon or Python to use them.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/11/2001; 2:30:14 PM (reads: 4226, responses: 0)
|
|
We have no argument here.
Of course, with enough syntactic abstraction and library support, it will be easy to be fooled into thinking we are programming in a new language
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/13/2001; 2:23:36 AM (reads: 4232, responses: 2)
|
|
Concerning language features vs. libraries, see the comp.lang.ada post.
|
|
Frank Atanassow - Re: Charming Python : Iterators and simple generators
10/13/2001; 5:22:32 AM (reads: 4335, responses: 1)
|
|
Can't say I agree with the sentiment expressed in this post. The author is just claiming that features (functionality) expressed as library imports are worse than primitive language features because libraries are not designed as well as languages. So the answer is: write better libraries.
I guess he is also saying that most libraries don't "cohere" as well as languages, but that can also be regarded as a flaw in the language: it's not expressive enough to let you structure your libraries in a sensible way. More expressive languages address this issue, though.
In the Haskell world, combinator-style libraries are popular and, I think, hang together very well precisely because Haskell libraries can exploit Haskell's features. There are a lot of things you can tell about a library function, for example, by looking at its type, which you would have to bury in the documentation if you were using a less expressive language. For example, you can tell whether a function has side-effects (more accurately, whether it returns an action) or not by checking if the return type is monadic. And, generally speaking, referential transparency lets you eliminate a lot of provisos and special cases which would normally need to be mentioned in documentation.
What I'm trying to say is that, because Haskell enforces these kinds of properties automatically, the libraries tend naturally to become more uniform anyway. Admittedly, this is just shifting the problem to a new level: Haskell libraries are uniform exactly because certain features (static typing, referential transparency) are institutionalized. But those institutionalized features are pretty orthogonal, whereas built-ins like print statements, generators, do-while loops and so on usually overlap very heavily with other features.
|
|
Ehud Lamm - Re: Charming Python : Iterators and simple generators
10/14/2001; 4:08:07 AM (reads: 4435, responses: 0)
|
|
I am not sure I agree with him entirely myself, but I know where he is coming from...
You are more interested in the fundamental capabilites of the language, where as he writes about usabiity.
Natrually, you can design appropriate libraries, in languages that support syntactic abstracton as well as mechanisms that allow you to play with control flow. It is also true that you can use this criterion to judge the expressive powers of different languages. And Scheme will win big.
It is also true that in practice, it is many times easier to find languages more appropriate to the task at hand, than it is to find useful libraries. This statement may seem a bit extreme, but I think it is corroborated by practice.
Perhaps this is not as it should be, but it is the way things are. Sad but true.
|
|
|
|