Lukewarm about range literals
I chatted with some PythonLabs folks this morning and nobody had any real enthusiasm for range literals. I notice that: for i in [:100]: print i looks a bit too much like line noise. I remember that Randy Pausch once mentioned that a typical newbie will read this as: for i in 100 print i and they will have a heck of a time to reconstruct the punctuation, with all sorts of errors lurking, e.g.: for i in [100]: print i for i in [100:]: print i for i in :[100]: print i Is there anyone who wants to champion this? Sorry, Thomas! I'm not doing this to waste your time! It honestly only occurred to me this morning, after Tim mentioned he was at most lukewarm about it... --Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)
On Mon, Aug 28, 2000 at 04:59:36PM -0500, Guido van Rossum wrote:
Sorry, Thomas! I'm not doing this to waste your time! It honestly only occurred to me this morning, after Tim mentioned he was at most lukewarm about it...
Heh, no problem. It was good practice, and if you remember (or search your
mail archive) I was only lukewarm about it, too, back when you asked me to
write it! And been modulating between 'lukewarm' and 'stonecold', inbetween
generators, tuple-ranges that look like hardware-addresses, nonint-ranges
and what not.
Less-docs-to-write-if-noone-champions-this-then-ly y'rs,
--
Thomas Wouters
Just brain-dumping here: Thomas did an excellent job on the patch! It's clean & crisp and, I think, bulletproof. Just want that to be clear. As the reviewer, I spent about 2 hours playing with it, trying it out in my code. And I simply liked it less the more I used it; e.g., for i in [:len(a)]: a[i] += 1 struck me as clumsier and uglier than for i in range(len(a)): a[i] += 1 at once-- which I expected due to the novelty --but didn't grow on me at *all*. Which is saying something, since I'm the world's longest-standing fan of "for i indexing a" <wink>; i.e., I'm *no* fan of the range(len(...)) business, and this seems even worse. Despite that I should know 100x better at all levels, I kept finding myself trying to write stuff like for i in [:a]: # or [len(a)] a couple times, even [a:] once a[i] += 1 Charles likes slices. Me too! I *love* them. But as a standalone notation (i.e., not as a subscript), part of the glory of slicing breaks down: for the list a, a[:] makes good sense, but when *iterating* over a, it's suddenly [:len(a)] because there's no context to supply a correct upper bound. For 2.0, the question is solely yes-or-no on this specific notation. If it goes in, it will never go away. I was +0 at first, at best -0 now. It does nothing for me I can't do just as easily-- and I think more clearly --with range. The kinds of "extensions"/variations mentioned in the PEP make me shiver, too. Post 2.0, who knows. I'm not convinced Python actually needs another arithmetic-progression *list* notation. If it does, I've always been fond of Haskell's range literals (but note that they include the endpoint): Prelude> [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> [1, 3 .. 10] [1,3,5,7,9] Prelude> [10, 9 .. 1] [10,9,8,7,6,5,4,3,2,1] Prelude> [10, 7 .. -5] [10,7,4,1,-2,-5] Prelude> Of course Haskell is profoundly lazy too, so "infinite" literals are just as normal there: Prelude> take 5 [1, 100 ..] [1,100,199,298,397] Prelude> take 5 [3, 2 ..] [3,2,1,0,-1] It's often easier to just list the first two terms than to figure out the *last* term and name the stride. I like notations that let me chuckle "hey, you're the computer, *you* figure out the silly details" <wink>.
Tim Peters writes:
As the reviewer, I spent about 2 hours playing with it, trying it out in my code. And I simply liked it less the more I used it
That's 2 hours more than I (and probably most other people) spent trying it out.
For 2.0, the question is solely yes-or-no on this specific notation. If it goes in, it will never go away.
This strikes me as an extremely strong argument. If the advantages aren't really all that clear, then adopting this syntax for range literals now removes the possibility to come up with a better way at a later date ("opportunity cost", as the economists say). The Haskell examples you shared are pretty neat. FWIW, I retract my earlier +1.
On Mon, 28 Aug 2000, Tim Peters wrote:
Post 2.0, who knows. I'm not convinced Python actually needs another arithmetic-progression *list* notation. If it does, I've always been fond of Haskell's range literals (but note that they include the endpoint):
Prelude> [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> [1, 3 .. 10] [1,3,5,7,9] Prelude> [10, 9 .. 1] [10,9,8,7,6,5,4,3,2,1] Prelude> [10, 7 .. -5] [10,7,4,1,-2,-5]
I think these examples are beautiful. There is no reason why we couldn't fit something like this into Python. Imagine this: - The ".." operator produces a tuple (or generator) of integers. It should probably have precedence just above "in". - "a .. b", where a and b are integers, produces the sequence of integers (a, a+1, a+2, ..., b). - If the left argument is a tuple of two integers, as in "a, b .. c", then we get the sequence of integers from a to c with step b-a, up to and including c if c-a happens to be a multiple of b-a (exactly as in Haskell). And, optionally: - The "..!" operator produces a tuple (or generator) of integers. It functions exactly like the ".." operator except that the resulting sequence does not include the endpoint. (If you read "a .. b" as "go from a up to b", then read "a ..! b" as "go from a up to, but not including b".) If this operator existed, we could then write: for i in 2, 4 .. 20: print i for i in 1 .. 10: print i*i for i in 0 ..! len(a): a[i] += 1 ...and these would all do the obvious things. -- ?!ng
Ka-Ping Yee
for i in 1 .. 10: print i*i
That looks quite nice to me!
for i in 0 ..! len(a): a[i] += 1
And that looks quite ugly. Couldn't it just as well be for i in 0 .. len(a)-1: a[i] += 1 and be vastly clearer? Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
"GE" == Greg Ewing
writes:
GE> Ka-Ping Yee
Ka-Ping Yee
: for i in 1 .. 10: print i*i for i in 0 ..! len(a): a[i] += 1
Greg Wilson writes: The problem with using ellipsis is that there's no obvious way to include a stride --- how do you hit every second (or n'th) element, rather than every element? I'd rather stick to range() than adopt: for i in [1..10:5] Thanks, Greg BTW, I understand from side conversations that adding a 'keys()' method to sequences, so that arbitrary collections could be iterated over using: for i in S.keys(): print i, S[i] was considered and rejected. If anyone knows why, I'd be grateful for a recap.
Greg Wilson wrote:
BTW, I understand from side conversations that adding a 'keys()' method to sequences, so that arbitrary collections could be iterated over using:
for i in S.keys(): print i, S[i]
was considered and rejected. If anyone knows why, I'd be grateful for a recap.
If I remember correctly, it was rejected because adding keys(), items() etc. methods to sequences would make all objects (in this case sequences and mappings) look the same. More accurate information from: http://sourceforge.net/patch/?func=detailpatch&patch_id=101178&group_id=5470 Peter -- Peter Schneider-Kamp ++47-7388-7331 Herman Krags veg 51-11 mailto:peter@schneider-kamp.de N-7050 Trondheim http://schneider-kamp.de
On Tue, 29 Aug 2000, Greg Wilson wrote:
The problem with using ellipsis is that there's no obvious way to include a stride --- how do you hit every second (or n'th) element, rather than every element?
As explained in the examples i posted, 1, 3 .. 20 could produce (1, 3, 5, 7, 9, 11, 13, 15, 17, 19) -- ?!ng
On Tue, 29 Aug 2000, Barry A. Warsaw wrote:
"KY" == Ka-Ping Yee
writes: KY> As explained in the examples i posted,
KY> 1, 3 .. 20
What would
1, 3, 7 .. 99
consider: rangeRecognizers.register( primeHandler ) rangeRecognizers.register( fibHandler ) rangeRecognizers.register( compositeHandler ) rangeRecognizers.register( randomHandler ) (you want to fall back on random handler last so it needs to be registered last) Paul Prescod
On Tue, 29 Aug 2000, Barry A. Warsaw wrote:
What would
1, 3, 7 .. 99
do? :)
Ka-Ping Yee writes:
ValueError: too many elements on left side of ".." operator
...
ValueError: at most two elements permitted on left side of ".."
Looks like a SyntaxError to me. ;) -Fred -- Fred L. Drake, Jr. <fdrake at beopen.com> BeOpen PythonLabs Team Member
On Tue, 29 Aug 2000, Fred L. Drake, Jr. wrote:
Ka-Ping Yee writes:
ValueError: too many elements on left side of ".." operator
...
ValueError: at most two elements permitted on left side of ".."
Looks like a SyntaxError to me. ;)
I would have called "\xgh" a SyntaxError too, but Guido argued convincingly that it's consistently ValueError for bad literals. So i'm sticking with that. See the thread of replies to http://www.python.org/pipermail/python-dev/2000-August/014629.html -- ?!ng
Ka-Ping Yee writes:
I would have called "\xgh" a SyntaxError too, but Guido argued convincingly that it's consistently ValueError for bad literals.
I understand the idea about bad literals. I don't think that's what this is. -Fred -- Fred L. Drake, Jr. <fdrake at beopen.com> BeOpen PythonLabs Team Member
On 29 August 2000, Ka-Ping Yee said:
I think these examples are beautiful. There is no reason why we couldn't fit something like this into Python. Imagine this:
- The ".." operator produces a tuple (or generator) of integers. It should probably have precedence just above "in".
- "a .. b", where a and b are integers, produces the sequence of integers (a, a+1, a+2, ..., b).
- If the left argument is a tuple of two integers, as in "a, b .. c", then we get the sequence of integers from a to c with step b-a, up to and including c if c-a happens to be a multiple of b-a (exactly as in Haskell).
I guess I haven't been paying much attention, or I would have squawked at the idea of using *anything* other than ".." for a literal range.
If this operator existed, we could then write:
for i in 2, 4 .. 20: print i
for i in 1 .. 10: print i*i
Yup, beauty. +1 on this syntax. I'd vote to scuttle the [1..10] patch and wait for an implementation of The Right Syntax, as illustrated by Ping.
for i in 0 ..! len(a): a[i] += 1
Ugh. I agree with everythone else on this: why not "0 .. len(a)-1"? Greg
On Wed, Aug 30, 2000 at 01:52:36PM -0400, Greg Ward wrote:
I'd vote to scuttle the [1..10] patch and wait for an implementation of The Right Syntax, as illustrated by Ping.
There *is* no [1..10] patch. There is only the [1:10] patch. See the PEP ;)
--
Thomas Wouters
tim peters wrote:
Charles likes slices. Me too! I *love* them. But as a standalone notation (i.e., not as a subscript), part of the glory of slicing breaks down: for the list a, a[:] makes good sense, but when *iterating* over a, it's suddenly [:len(a)] because there's no context to supply a correct upper bound.
agreed. ranges and slices are two different things. giving them the same syntax is a lousy idea.
Post 2.0, who knows. I'm not convinced Python actually needs another arithmetic-progression *list* notation. If it does, I've always been fond of Haskell's range literals (but note that they include the endpoint):
Prelude> [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> [1, 3 .. 10] [1,3,5,7,9]
isn't that taken from SETL? (the more I look at SETL, the more Pythonic it looks. not too bad for something that was designed in the late sixties ;-) talking about SETL, now that the range literals are gone, how about revisiting an old proposal: "...personally, I prefer their "tuple former" syntax over the the current PEP202 proposal: [expression : iterator] [n : n in range(100)] [(x**2, x) : x in range(1, 6)] [a : a in y if a > 5] (all examples are slightly pythonified; most notably, they use "|" or "st" (such that) instead of "if") the expression can be omitted if it's the same thing as the loop variable, *and* there's at least one "if" clause: [a in y if a > 5] also note that their "for-in" statement can take qualifiers: for a in y if a > 5: ... </F>
[/F]
agreed. ranges and slices are two different things. giving them the same syntax is a lousy idea.
Don't know about *that*, but it doesn't appear to work as well as was hoped. [Tim]
Post 2.0, who knows. I'm not convinced Python actually needs another arithmetic-progression *list* notation. If it does, I've always been fond of Haskell's range literals (but note that they include the endpoint):
Prelude> [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> [1, 3 .. 10] [1,3,5,7,9]
isn't that taken from SETL?
Sure looks like it to me. The Haskell designers explicitly credited SETL for list comprehensions, but I don't know that they do for this gimmick too. Of course Haskell's "infinite" list builders weren't in SETL, and, indeed, expressions like [1..] are pretty common in Haskell programs. One of the prettiest programs ever in any language ever: primes = sieve [2..] where sieve (x:xs) = x : sieve [n | n <- xs, n `mod` x /= 0] which defines the list of all primes.
(the more I look at SETL, the more Pythonic it looks. not too bad for something that was designed in the late sixties ;-)
It was way ahead of its time. Still is! Check out its general loop construct, though -- now *that's* a kitchen sink. Guido mentioned that ABC's Lambert Meertens spent a year's sabbatical at NYU when SETL was in its heyday, and I figure that's where ABC got quantifiers in boolean expressions (if each x in list has p(x); if no x in list has p(x); if some x in list has p(x)). Have always wondered why Python didn't have that too; I ask that every year, but so far Guido has never answered it <wink>.
talking about SETL, now that the range literals are gone, how about revisiting an old proposal:
"...personally, I prefer their "tuple former" syntax over the the current PEP202 proposal:
[expression : iterator]
[n : n in range(100)] [(x**2, x) : x in range(1, 6)] [a : a in y if a > 5]
(all examples are slightly pythonified; most notably, they use "|" or "st" (such that) instead of "if")
the expression can be omitted if it's the same thing as the loop variable, *and* there's at least one "if" clause:
[a in y if a > 5]
also note that their "for-in" statement can take qualifiers:
for a in y if a > 5: ...
You left off the last sentence from the first time you posted this:
is there any special reason why we cannot use colon instead of "for"?
Guido then said we couldn't use a colon because that would make [x : y] too hard to parse, because range literals were of the same form. Thomas went on to point out that it's worse than that, it's truly ambiguous. Now I expect you prefaced this with "now that the range literals are gone" expecting that everyone would just remember all that <wink>. Whether they did or not, now they should. I counted two replies beyond those. One from Peter Schneider-Kamp was really selling another variant. The other from Marc-Andre Lemburg argued that while the shorthand is convenient for mathematicians, "I doubt that CP4E users get the grasp of this". Did I miss anything? Since Guido didn't chime in again, I assumed he was happy with how things stood. I further assume he picked on a grammar technicality to begin with because that's the way he usually shoots down a proposal he doesn't want to argue about -- "no new keywords" has served him extremely well that way <wink>. That is, I doubt that "now that the range literals are gone" (if indeed they are!) will make any difference to him, and with the release one week away he'd have to get real excited real fast. I haven't said anything about it, but I'm with Marc-Andre on this: sets were *extremely* heavily used in SETL, and brevity in their expression was a great virtue there because of it. listcomps won't be that heavily used in Python, and I think it's downright Pythonic to leave them wordy in order to *discourage* fat hairy listcomp expressions. They've been checked in for quite a while now, and I like them fine as they are in practice. I've also got emails like this one in pvt: The current explanation "[for and if clauses] nest in the same way for loops and if statements nest now." is pretty clear and easy to remember. That's important too, because despite pockets of hysteria to the contrary on c.l.py, this is still Python. When I first saw your first example: [n : n in range(100)] I immediately read "n in range(100)" as a true/false expression, because that's what it *is* in 1.6 unless immediately preceded by "for". The current syntax preserves that. Saving two characters (":" vs "for") isn't worth it in Python. The vertical bar *would* be "worth it" to me, because that's what's used in SETL, Haskell *and* common mathematical practice for "such that". Alas, as Guido is sure to point out, that's too hard to parse <0.9 wink>. consider-it-channeled-unless-he-thunders-back-ly y'rs - tim
On Tue, Aug 29, 2000 at 05:45:24AM -0400, Tim Peters wrote:
Saving two characters (":" vs "for") isn't worth it in Python. The vertical bar *would* be "worth it" to me, because that's what's used in SETL, Haskell *and* common mathematical practice for "such that". Alas, as Guido is sure to point out, that's too hard to parse
It's impossible to parse, of course, unless you require the parentheses
around the expression preceding it :)
[ (n) | n in range(100) if n%2 ]
I-keep-writing-'where'-instead-of-'if'-in-those-ly y'rs,
--
Thomas Wouters
thomas wrote:
Saving two characters (":" vs "for") isn't worth it in Python. The vertical bar *would* be "worth it" to me, because that's what's used in SETL, Haskell *and* common mathematical practice for "such that". Alas, as Guido is sure to point out, that's too hard to parse
It's impossible to parse, of course, unless you require the parentheses around the expression preceding it :)
[ (n) | n in range(100) if n%2 ]
I'm pretty sure Tim meant "|" instead of "if". the SETL syntax is: [ n : n in range(100) | n%2 ] (that is, ":" instead of for, and "|" or "st" instead of "if". and yes, they have nice range literals too, so don't take that "range" too literal ;-) in SETL, that can also be abbreviated to: [ n in range(100) | n%2 ] which, of course, is a perfectly valid (though slightly obscure) python expression... </F>
isn't that taken from SETL?
(the more I look at SETL, the more Pythonic it looks. not too bad for something that was designed in the late sixties ;-)
You've got it backwards: Python's predecessor, ABC, was inspired by SETL -- Lambert Meertens spent a year with the SETL group at NYU before coming up with the final ABC design! --Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)
It was way ahead of its time. Still is! Check out its general loop construct, though -- now *that's* a kitchen sink. Guido mentioned that ABC's Lambert Meertens spent a year's sabbatical at NYU when SETL was in its heyday, and I figure that's where ABC got quantifiers in boolean expressions (if each x in list has p(x); if no x in list has p(x); if some x in list has p(x)). Have always wondered why Python didn't have that too; I ask that every year, but so far Guido has never answered it <wink>.
I don't recall you asking me that even *once* before now. Proof, please? Anyway, the answer is that I saw diminishing returns from adding more keywords and syntax. --Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)
[Tim]
... Have always wondered why Python didn't have that [ABC's boolean quatifiers] too; I ask that every year, but so far Guido has never answered it <wink>.
[Guido]
I don't recall you asking me that even *once* before now. Proof, please?
That's too time-consuming until DejaNews regains its memory. I never asked *directly*, it simply comes up at least once a year on c.l.py (and has since the old days!), and then I always mention that it comes up every year but that Guido never jumps into those threads <wink>. The oldest reference I can find in DejaNews today is just from January 1st of this year, at the end of http://www.deja.com/getdoc.xp?AN=567219971 There it got mentioned offhandedly. Much earlier threads were near-PEP'ish in their development of how this could work in Python. I'll attach the earliest one I have in my personal email archive, from a bit over 4 years ago. All my personal email much before that got lost in KSR's bankruptcy bit bucket.
Anyway, the answer is that I saw diminishing returns from adding more keywords and syntax.
Yes, I've channeled that too -- that's why I never bugged you directly <wink>. -----Original Message----- From: python-list-request@cwi.nl [mailto:python-list-request@cwi.nl] Sent: Saturday, August 03, 1996 4:42 PM To: Marc-Andre Lemburg; python-list@cwi.nl Subject: RE: \exists and \forall in Python ?!
[Marc-Andre Lemburg] ... [suggesting "\exists" & "\forall" quantifiers] ...
Python took several ideas from CWI's ABC language, and this is one that didn't make the cut. I'd be interested to hear Guido's thoughts on this! They're certainly very nice to have, although I wouldn't say they're of core importance. But then a lot of "nice to have but hardly crucial" features did survive the cut (like, e.g., "x < y < z" as shorthand for "x < y and y < z"), and it's never clear where to draw the line. In ABC, the additional keywords were "some", "each", "no" and "has", as in (importing the ABC semantics into a virtual Python): if some d in range(2,n) has n % d == 0: print n, "not prime; it's divisible by", d else: print n, "is prime" or if no d in range(2,n) has n % d == 0: print n, "is prime" else: print n, "not prime; it's divisible by", d or if each d in range(2,n) has n % d == 0: print n, "is <= 2; test vacuously true" else: print n, "is not divisible by, e.g.,", d So "some" is a friendly spelling of "there exists", "no" of "not there exists", and "each" of "for all". In addition to testing the condition, "some" also bound the test vrbls to "the first" witness if there was one, and "no" and "each" to the first counterexample if there was one. I think ABC got that all exactly right, so (a) it's the right model to follow if Python were to add this, and (b) the (very useful!) business of binding the test vrbls if & only if the test succeeds (for "some") or fails (for "no" and "each") makes it much harder to fake (comprehensibly & efficiently) via map & reduce tricks. side-effects-are-your-friends-ly y'rs - tim Tim Peters tim_one@msn.com, tim@dragonsys.com not speaking for Dragon Systems Inc.
One of the original arguments for range literals as I recall was that indexing of loops could get more efficient. The compiler would know that [0:100:2] represents a series of integers and could conceivably generate more efficient loop indexing code (and so could Python2C and other compilers that generated C code). This argument doesn't seem to be showing up here at all. Does it carry no weight in the face of the relative inscrutability of the syntax? Skip
[Skip Montanaro]
One of the original arguments for range literals as I recall was that indexing of loops could get more efficient. The compiler would know that [0:100:2] represents a series of integers and could conceivably generate more efficient loop indexing code (and so could Python2C and other compilers that generated C code). This argument doesn't seem to be showing up here at all. Does it carry no weight in the face of the relative inscrutability of the syntax?
It carries no weight at all *for 2.0* because the patch didn't exploit the efficiency possibilities. Which I expect are highly overrated (maybe 3% in a "good case" real-life loop) anyway. Even if they aren't, the same argument would apply to any other new syntax for this too, so in no case is it an argument in favor of this specific new syntax over alternative new syntaxes. There are also well-known ways to optimize the current "range" exactly the way Python works today; e.g., compile two versions of the loop, one assuming range is the builtin, the other assuming it may be anything, then a quick runtime test to jump to the right one. Guido hates that idea just because it's despicable <wink>, but that's the kind of stuff optimizing compilers *do*, and if we're going to get excited about efficiency then we need to consider *all sorts of* despicable tricks like that. In any case, I've spent 5 hours straight now digging thru Python email, have more backed up than when I started, and have gotten nothing done today toward moving 2.0b1 along. I'd love to talk more about all this, but there simply isn't the time for it now ...
Guido> I notice that: Guido> for i in [:100]: print i Guido> looks a bit too much like line noise. I remember that Randy Guido> Pausch once mentioned that a typical newbie will read this as: Guido> for i in 100 print i Just tossing out a couple ideas here. I don't see either mentioned in the current version of the PEP. 1. Would it help readability if there were no optional elements in range literals? That way you'd have to write for i in [0:100]: print i 2. Would it be more visually obvious to use ellipsis notation to separate the start and end inidices? >>> for i in [0...100]: print i 0 1 ... 99 >>> for i in [0...100:2]: print i 0 2 ... 98 I don't know if either are possible syntactically. Skip
On Mon, Aug 28, 2000 at 04:46:19PM -0500, Skip Montanaro wrote:
I don't know if either are possible syntactically.
They are perfectly possible (in fact, more easily so than the current
solution, if it hadn't already been written.) I like the elipsis syntax
myself, but mostly because i have *no* use for elipses, currently. It's also
reminiscent of the range-creating '..' syntax I learned in MOO, a long time
ago ;)
--
Thomas Wouters
Thomas Wouters wrote: They are perfectly possible (in fact, more easily so than the current solution, if it hadn't already been written.) I like the elipsis syntax myself, but mostly because i have *no* use for elipses, currently. It's also reminiscent of the range-creating '..' syntax I learned in MOO, a long time ago ;)
I would vote -1 on [0...100:10] --- even range(0, 100, 10) reads better, IMHO. I understand Guido et al's objections to: for i in [:100]: but in my experience, students coming to Python from other languages seem to expect to be able to say "do this N times" very simply. Even: for i in range(100): raises eyebrows. I know it's all syntactic sugar, but it comes up in the first hour of every course I've taught... Thanks, Greg
Greg Wilson wrote:
I would vote -1 on [0...100:10] --- even range(0, 100, 10) reads better,
I don't like [0...100] either. It just looks bad. But I really *do* like [0..100] (maybe that's Pascal being my first serious language). That said, I prefer almost any form of range literals over the current situation. range(0,100) has no meaning to me (maybe because English is not my mother tongue), but [0..100] looks like "from 0 to 100" (although one might expect len([1..100]) == 100).
but in my experience, students coming to Python from other languages seem to expect to be able to say "do this N times" very simply. Even:
for i in range(100):
raises eyebrows. I know it's all syntactic sugar, but it comes up in the first hour of every course I've taught...
I fully agree on that one, although I think range(N) to iterate N times isn't as bad as range(len(SEQUENCE)) to iterate over the indices of a sequence. not-voting---but-you-might-be-able-to-guess-ly y'rs Peter -- Peter Schneider-Kamp ++47-7388-7331 Herman Krags veg 51-11 mailto:peter@schneider-kamp.de N-7050 Trondheim http://schneider-kamp.de
Peter> I don't like [0...100] either. It just looks bad. But I really Peter> *do* like [0..100] (maybe that's Pascal being my first serious Peter> language). Which was why I proposed "...". It's sort of like "..", but has the advantage of already being a recognized token. I doubt there would be much problem adding ".." as a token either. What we really want I think is something that evokes the following in the mind of the reader for i from START to END incrementing by STEP: without gobbling up all those keywords. That might be one of the following: for i in [START..END,STEP]: for i in [START:END:STEP]: for i in [START..END:STEP]: I'm sure there are other possibilities, but given the constraints of putting the range literal in square brackets and not allowing a comma as the first separator, the choices seem limited. Perhaps it will just have to wait until Py3K when a little more grammar fiddling is possible. Skip
On Tue, Aug 29, 2000 at 09:46:23AM -0500, Skip Montanaro wrote:
Which was why I proposed "...". It's sort of like "..", but has the advantage of already being a recognized token. I doubt there would be much problem adding ".." as a token either.
"..." is not a token, it's three tokens:
subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
So adding ".." should be no problem.
--
Thomas Wouters
Thomas Wouters writes:
On Tue, Aug 29, 2000 at 09:46:23AM -0500, Skip Montanaro wrote:
Which was why I proposed "...". It's sort of like "..", but has the advantage of already being a recognized token. I doubt there would be much problem adding ".." as a token either.
"..." is not a token, it's three tokens:
subscript: '.' '.' '.' | test | [test] ':' [test] [sliceop]
So adding ".." should be no problem.
I have another idea. I don't think it's been discussed previously, but I came late to this party. Sorry if this is old hat. How about a:b to indicate the range starting at a and ending with b-1? I claim that this syntax is already implicit in Python. Think about the following: if S is a sequence and i an index, S[i] means the pairing of the sequence S with the index i. Sequences and indices are `dual' in the sense that pairing them together yields a value. I am amused by the fact that in the C language, S[i] = *(S+i) = *(i+S) = i[S] which really shows this duality. Now we already have S[a:b] to denote the slice operation, but this can also be described as the pairing of S with the range literal a:b According to this view, the square braces indicate the pairing or mapping operation itself, they are not part of the range literal. They shouldn't be part of the range literal syntax. Thinking about this gets confused by the additional use of `[' for list construction. If you take them away, you could even defend having 1:5 create an xrange-like object rather than a list. I think this also shows why [a:b] is *not* the natural syntax for a range literal. This is beautfully symmetric to me - 1..3 looks like it should be a closed interval (including the endpoints), but it's very natural and Pythonic that a:b is semi-open: the existing "slice invariance" S[a:b] + S[b:c] = S[a:c] could be expressed as a:b + b:c = a:c which is very attractive to me, but of course there are problems. The syntax Tim disfavored: for i in [:len(a)]: now becomes for i in 0:len(a): #do not allow elided endpoints outside of a [ context which doesn't look so bad to me, but is probably ambiguous. Hmmm, could this possibly work or is it too much of a collision with the use of `:' to indicate block structure? Tim - I agree that the Haskell prime-number printing program is indeed one of the prettiest programs ever. Thanks for posting it. Hold-off-on-range-literals-for-2.0-ly yr's, -C
On Tue, 29 Aug 2000, Charles G Waldman wrote:
I have another idea. I don't think it's been discussed previously, but I came late to this party. Sorry if this is old hat.
How about a:b to indicate the range starting at a and ending with b-1?
I think it's nice. I'm not sure I like it yet, but it's an interesting
idea. Someone's gonna yell ": is ambiguos". Well, you know how, when
you know Python, you go around telling people "() don't create tuples,
commas do" and feeling all wonderful? Well, we can do the same with
ranges <wink>.
(:)-ly y'rs, Z.
--
Moshe Zadka
How about a:b to indicate the range starting at a and ending with b-1?
I believe this is what the Nummies originally suggested.
which doesn't look so bad to me, but is probably ambiguous. Hmmm, could this possibly work or is it too much of a collision with the use of `:' to indicate block structure?
Alas, it could never work. Look at this: for i in a:b:c Does it mean for i in (a:b) : c or for i in a: (b:c) ? So we're back to requiring *some* form of parentheses. I'm postponing this discussion until after Python 2.0 final is released -- the feature freeze is real! --Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)
Guido van Rossum writes:
Alas, it could never work. Look at this:
for i in a:b:c
Does it mean
for i in (a:b) : c
or
for i in a: (b:c)
Of course, it means "for i in the range from a to b-1 with stride c", but as written it's illegal because you'd need another `:' after the c. <wink>
I'm postponing this discussion until after Python 2.0 final is released -- the feature freeze is real!
Absolutely. I won't bring this up again, until the approprate time.
I doubt there would be much problem adding ".." as a token either.
If we're going to use any sort of ellipsis syntax here, I think it would be highly preferable to use the ellipsis token we've already got. I can't see any justification for having two different ellipsis-like tokens in the language, when there would be no ambiguity in using one for both purposes.
What we really want I think is something that evokes the following in the mind of the reader
for i from START to END incrementing by STEP:
Am I right in thinking that the main motivation here is to clean up the "for i in range(len(a))" idiom? If so, what's wrong with a built-in: def indices(a): return range(len(a)) Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
Greg Ewing wrote:
Am I right in thinking that the main motivation here is to clean up the "for i in range(len(a))" idiom? If so, what's wrong with a built-in:
def indices(a): return range(len(a))
As far as I know adding a builtin indices() has been rejected as an idea. Peter -- Peter Schneider-Kamp ++47-7388-7331 Herman Krags veg 51-11 mailto:peter@schneider-kamp.de N-7050 Trondheim http://schneider-kamp.de
Peter Schneider-Kamp
As far as I know adding a builtin indices() has been rejected as an idea.
But why? I know it's been suggested, but I don't remember seeing any convincing arguments against it. Or much discussion at all. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
On Thu, 31 Aug 2000, Greg Ewing wrote:
Peter Schneider-Kamp
: As far as I know adding a builtin indices() has been rejected as an idea.
But why? I know it's been suggested, but I don't remember seeing any convincing arguments against it. Or much discussion at all.
I submitted a patch to add indices() and irange() previously. See: http://sourceforge.net/patch/?func=detailpatch&patch_id=101129&group_id=5470 Guido rejected it: gvanrossum: 2000-Aug-17 12:16 I haven't seen the debate! But I'm asked to pronounce anyway, and I just don't like this. Learn to write code that doesn't need the list index! tim_one: 2000-Aug-15 15:08 Assigned to Guido for Pronouncement. The debate's been debated, close it out one way or the other. ping: 2000-Aug-09 03:00 There ya go. I have followed the style of the builtin_range() function, and docstrings are included. -- ?!ng
greg wrote:
If we're going to use any sort of ellipsis syntax here, I think it would be highly preferable to use the ellipsis token we've already got. I can't see any justification for having two different ellipsis-like tokens in the language, when there would be no ambiguity in using one for both purposes.
footnote: "..." isn't really token:
class Spam: ... def __getitem__(self, index): ... print index ... spam = Spam() spam[...] Ellipsis spam[. . .] Ellipsis spam[. ... . ... . ... ] Ellipsis
(etc) </F>
Fredrik Lundh
footnote: "..." isn't really token:
Whatever it is technically, it's an existing part of the language, and it seems redundant and confusing to introduce another very similar one. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
[Skip Montanaro]
... What we really want I think is something that evokes the following in the mind of the reader
for i from START to END incrementing by STEP:
without gobbling up all those keywords.
Note that they needn't be keywords, though, any more than "as" became a keyword in the new "import x as y". I love the Haskell notation in Haskell because it fits so nicely with "infinite" lists there too. I'm not sure about in Python -- 100s of languages have straightforward integer index generation, and Python's range(len(seq)) is hard to see as much more than gratuitous novelty when viewed against that background. for i = 1 to 10: # 1 to 10 inclusive for i = 10 to 1 by -1: # 10 down to 1 inclusive for i = 1 upto 10: # 1 to 9 inclusive for i = 10 upto 1 by -1: # 10 down to 2 inclusive are all implementable right now without new keywords, and would pretty much *have* to be "efficient" from the start because they make no pretense at being just one instance of an infinitely extensible object iteration protocol. They are what they are, and that's it -- simplicity isn't *always* a bad thing <wink>.
for i in [START..END,STEP]: for i in [START:END:STEP]: for i in [START..END:STEP]:
The difference in easy readability should squawk for itself.
for i in 0 ..! len(a): a[i] += 1
Looks like everybody hates that, and that's understandable, but I can't imagine why for in 0 .. len(a)-1: isn't *equally* hated! Requiring "-1" in the most common case is simply bad design. Check out the Python-derivative CORBAscript, where Python's "range" was redefined to *include* the endpoint. Virtually every program I've seen in it bristles with ugly for i in range(len(a)-1) lines. Yuck. but-back-to-2.0-ly y'rs - tim
Tim Peters
I can't imagine why
for in 0 .. len(a)-1:
isn't *equally* hated! Requiring "-1" in the most common case is simply bad design.
I agree with that. I didn't mean to suggest that I thought it was a good idea. The real problem is in defining a..b to include b, which gives you a construct that is intuitive but not very useful in the context of the rest of the language. On the other hand, if a..b *doesn't* include b, it's more useful, but less intuitive. (By "intuitive" here, I mean "does what you would expect based on your experience with similar notations in other programming languages or in mathematics".) I rather like the a:b idea, because it ties in with the half-open property of slices. Unfortunately, it gives the impression that you should be able to say a = [1,2,3,4,5,6] b = 2:5 c = a[b] and get c == [3,4,5].
for i = 1 to 10: # 1 to 10 inclusive
Endpoint problem again. You would be forever saying for i = 0 to len(a)-1: I do like the idea of keywords, however. All we need to do is find a way of spelling for i = 0 uptobutnotincluding len(a): without running out of breath. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+
I guess I'm in the minority here because I kind of like the range literal syntax. Guido van Rossum writes:
I notice that:
for i in [:100]: print i
looks a bit too much like line noise. I remember that Randy Pausch once mentioned that a typical newbie will read this as:
for i in 100 print i
When I was a complete Python newbie (back around 1994) I thought that the syntax l2 = l1[:] for copying lists looked pretty mysterious and weird. But after spending some time programming Python I've come to think that the slice syntax is perfectly natural. Should constructs be banned from the language simply because they might confuse newbies? I don't think so. I for one like Thomas' range literals. They fit very naturally into the existing Python concept of slices.
and they will have a heck of a time to reconstruct the punctuation, with all sorts of errors lurking, e.g.:
for i in [100]: print i for i in [100:]: print i for i in :[100]: print i
This argument seems a bit weak to me; you could take just about any Python expression and mess up the punctuation with misplaced colons.
Is there anyone who wants to champion this?
I don't know about "championing" it but I'll give it a +1, if that counts for anything.
Charles wrote: When I was a complete Python newbie (back around 1994) I thought that the syntax
l2 = l1[:]
for copying lists looked pretty mysterious and weird. But after spending some time programming Python I've come to think that the slice syntax is perfectly natural. Should constructs be banned from the language simply because they might confuse newbies?
Greg writes: Well, it *is* the reason we switched from Perl to Python in our software engineering course... Greg
Charles wrote: When I was a complete Python newbie (back around 1994) I thought that the syntax
l2 = l1[:]
for copying lists looked pretty mysterious and weird. But after spending some time programming Python I've come to think that the slice syntax is perfectly natural. Should constructs be banned from the language simply because they might confuse newbies?
Greg writes: Well, it *is* the reason we switched from Perl to Python in our software engineering course...
And the original proposal for range literals also came from the Numeric corner of the world (I believe Paul Dubois first suggested it to me). --Guido van Rossum (home page: http://www.pythonlabs.com/~guido/)
participants (16)
-
bwarsaw@beopen.com
-
Charles G Waldman
-
Fred L. Drake, Jr.
-
Fredrik Lundh
-
Fredrik Lundh
-
Greg Ewing
-
Greg Ward
-
Greg Wilson
-
Guido van Rossum
-
Ka-Ping Yee
-
Moshe Zadka
-
Paul
-
Peter Schneider-Kamp
-
Skip Montanaro
-
Thomas Wouters
-
Tim Peters