469,306 Members | 1,771 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,306 developers. It's quick & easy.

Arithmetic sequences in Python

Please visit http://www.python.org/peps/pep-0204.html first.

As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice. So, maybe
there's a reason to adopt slightly modified Haskell's syntax? Something
like

[1,3..10] --> [1,3,5,7,9]
(1,3..10) --> same values as above, but return generator instead of
list
[1..10] --> [1,2,3,4,5,6,7,8,9,10]
(1 ..) --> 'infinite' generator that yield 1,2,3 and so on
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on

So,
1) "[]" means list, "()" means generator
2) the "start" is required, "step" and "end" are optional.

Also, this can be nicely integrated with enumerations (if they will
appear in python). Haskell is also example of such integration.

Jan 16 '06 #1
72 5109
Bas
I like the use of the colon as in the PEP better: it is consistant with
the slice notation and also with the colon operator in Matlab.

I like the general idea and I would probably use it a lot if available,
but the functionality is already there with range and irange.

Bas

Jan 16 '06 #2
"Gregory Petrosyan" <gr***************@gmail.com> writes:
As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice. So, maybe
there's a reason to adopt slightly modified Haskell's syntax?


I like this with some issues: Python loops tend to be 0-based, so
while it's convenient to express the sequence [1..n], what you usually
want is [0..(n-1)] which is uglier.

If you want to count down from f(n) to zero, in Haskell you might say

[b, b-1 .. 0] where b=f(n)

There's no "where" in Python, so what do you do?

[f(n)-1, f(n)-2 .. 0]

evaluates f twice (and might not even get the same result both times),
while the traditional

xrange(f(n)-1, -1, -1)

only evaluates it once but is IMO repulsive.

Anyway I've never liked having to use range or xrange to control
Python loops. It's just kludgy and confusing. So I think your idea
has potential but there's issues that have to be worked out.
Jan 16 '06 #3
_Consistentsy_ is what BDFL rejects, if I understand pep right. As for
me, it's not too god to have similar syntax for really different tasks.
And [1:10] is really not obvious, while [1..10] is.

Jan 16 '06 #4
On Mon, 16 Jan 2006 01:01:39 -0800, Gregory Petrosyan wrote:
Please visit http://www.python.org/peps/pep-0204.html first.

As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice. So, maybe
there's a reason to adopt slightly modified Haskell's syntax? Something
like

[1,3..10] --> [1,3,5,7,9]


-1 on the introduction of new syntax. Any new syntax.

(I reserve the right to change my mind if somebody comes up with syntax
that I like, but in general, I'm *very* negative on adding to Python's
clean syntax.)

For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange. The only added feature this proposal
introduces is infinite iterators, and they aren't particularly hard to
make:

def arithmetic_sequence(start, step=1):
yield start
while 1:
start += step
yield start

The equivalent generator for a geometric sequence is left as an exercise
for the reader.

If your proposal included support for ranges of characters, I'd be more
interested.

--
Steven.

Jan 16 '06 #5
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange.
Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).
The only added feature this proposal
introduces is infinite iterators, and they aren't particularly hard to
make:

def arithmetic_sequence(start, step=1):
yield start
while 1:
start += step
yield start
Well, that would be itertools.count(start, step) but in general a simple
expression is nicer than 5 lines of code.
If your proposal included support for ranges of characters, I'd be more
interested.


There's something to be said for that. Should ['a'..'z'] be a list or
a string?
Jan 16 '06 #6
Paul Rubin wrote:
There's something to be said for that. Should ['a'..'z'] be a list or
a string?

To me, the most obvious result would be either a range object as a
result, or always a list/generator of objects (to stay perfectly
consistent). If a range of numbers translate into a list of numbers,
then a range of characters should likewise translate to a list of
characters, and a join would be required to get a regular string. This
also adds more consistency between the two proposals of the initial post
(e.g. list-based range and generator-based range), for while the
list-based range could be expanded into a string a generator-based one
couldn't/shouldn't, and the abstraction breaks (because two constructs
that should be more or less equivalent become extremely different and
can't be swapped transparently).

This would also be consistent with other languages providing a native
"range" object such as Ruby or or Ada ranges.

The only thing that bothers me about the initial proposal is that there
would not, in fact, be any "range object", but merely a syntactic sugar
for list/generator creation. Not that I really mind it, but, well,
syntactic sugar for the purpose of syntactic sugar really doesn't bring
much to the table.

For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods) excludes the end object of the
range definition ('a'..'z') is the range from 'a' to 'z' while
('a'...'z') only ranges from 'a' to 'y').
Jan 16 '06 #7
Ranges of letters are quite useful, they are used a lot in Delphi/Ada
languages:
"a", "b", "c", "d", "e"...

I like the syntax [1..n], it looks natural enough to me, but I think
the Ruby syntax with ... isn't much natural.
To avoid bugs the following two lines must have the same meaning:
[1..n-1]
[1..(n-1)]

If you don't want to change the Python syntax then maybe the
range/xrange can be extended for chars too:

xrange("a", "z")
range("z", "a", -1)

But for char ranges I think people usually don't want to stop to "y"
(what if you want to go to "z" too? This is much more common than
wanting to stop to "y"), so another possibility is to create a new
function like xrange that generates the last element too:

interval("a", "c") equals to iter("abc")
interval(1, 3) equals to iter([1,2,3])
interval(2, 0, -1) equals to iter([2,1,0])

I have created such interval function, and I use it now and then.

Bye,
bearophile

Jan 16 '06 #8
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:
For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods)


No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some folks
want to undermine that.

*shakes head in amazement*
--
Steven.

Jan 16 '06 #9
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
...
while the traditional

xrange(f(n)-1, -1, -1)

only evaluates it once but is IMO repulsive.


Yep, reversed(range(f(n))) is MUCH better.
Alex
Jan 16 '06 #10
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange.


Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).


But not easier than reversed(range(6)) [[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]
Alex
Jan 16 '06 #11
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote:
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:
For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods)


No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some folks
want to undermine that.

*shakes head in amazement*


Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).
Alex
Jan 16 '06 #12
On Mon, 16 Jan 2006 02:58:39 -0800, Paul Rubin wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange.


Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).


Only in isolation, and arguably not even then. Or do you think that Perl
is much easier to read than Python simply because you can write your
programs in fewer characters?

It looks too much like the list [5,4,0], and is easy to make typos:
[5,4,.0] gives you no syntax error but very different results.

The meaning isn't particular clear: is it supposed to be [start, stop,
step] (the natural expectation for those used to Python slices and ranges)
or [start, next, stop]? It is actually the second, but go back to the
original post by Gregory: after giving examples, he still wrongly
described his proposal as having a "step" parameter. There is no step
parameter -- the step is implied, by subtracting start from next. Such
confusion doesn't bode well.

Python indexing deliberately goes to one-past-the-end counting for a
reason: it helps prevent off-by-one signpost errors. This syntax goes
against that decision, and adds one more thing to memorise about Python:
the end index is not included in the list, except for arithmetic
sequences, where it is, sometimes but not necessarily. In [5,6,10] the end
index 10 is included; in [5,7,10] it isn't.

You've picked the most awkward example of range, I admit. But let's look
at a few others:

[0,..9] versus range(10)
[55, ...73] versus range(55, 74)
[1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, 295] versus range(55, 296, 10)

How often do you find yourself knowing the first two terms of a sequence
but not the step size anyway? Is that a common use case?
The only added feature this proposal
introduces is infinite iterators, and they aren't particularly hard to
make:

def arithmetic_sequence(start, step=1):
yield start
while 1:
start += step
yield start


Well, that would be itertools.count(start, step) but in general a simple
expression is nicer than 5 lines of code.


I didn't say that my generator was the only way to produce the required
result, I was pointing out how simple it is. Yes, itertools is the way to
go for this sort of thing.

If your proposal included support for ranges of characters, I'd be more
interested.


There's something to be said for that. Should ['a'..'z'] be a list or a
string?


It uses [ something ] syntax, so for consistency with lists and list
comprehensions it should be a list.

But a string would be more practical, since list(['a'..'z']) is easier and
more intuitive than ''.join(['a'..'z']). But I'm not sure that it is
*that* much more practical to deserve breaking the reader's expectation.

So I think the best thing would be to create itertools.chars('a', 'z') or
similar, not new syntax.
--
Steven.

Jan 16 '06 #13
Alex Martelli <al***@mail.comcast.net> wrote:
Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).


How about...

for i in (0..x]:
blah

Jan 16 '06 #14
Roy Smith <ro*@panix.com> wrote:
Alex Martelli <al***@mail.comcast.net> wrote:
Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).


How about...

for i in (0..x]:
blah


The difference between a round parenthesis and a square bracket can
EASILY be overlooked, depending partly on what font you're using.
Alex
Jan 16 '06 #15
In <dq**********@reader2.panix.com>, Roy Smith wrote:
Alex Martelli <al***@mail.comcast.net> wrote:
Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).


How about...

for i in (0..x]:
blah


That would break most editors "highlight matching brace" functionality.

Ciao,
Marc 'BlackJack' Rintsch
Jan 16 '06 #16
i would love to see a nice, clear syntax instead of
for i in xrange(start, stop, step): ...

because xrange is ugly and iteration over int sequences are important.
we don't need a range() alternative ( [0:10] or [0..10] )
(because no one would ever use range() if there were a nice
integer-for-loop)

there was a proposal (http://www.python.org/peps/pep-0284.html):
for start <= i < stop: ...
but in this way you cannot specify the step parameter and it has some
problems when used in list comprehension.

pep 204 like syntax would be:
for i in (start:stop:step): ...
it is much nicer than xrange, but probably it has some inconsistency
with slicing
(eg (:3)=xrange(3), (-3:)=itertools.count(-3), (:-3)=?, (3::-1)=? )

your .. approach:
for i in (start, start+step .. stop): ...
here start written down twice if it's referred by a name (and if start
is a function call it's evaluated twice)
imho without a step it looks nice:
for i in (start .. stop): ...
but a new syntax would be good only if it can entirely replace the old
one (which then can be made deprecated).

Jan 16 '06 #17
al***@mail.comcast.net (Alex Martelli) writes:
Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).
But not easier than reversed(range(6))


Heh, I like that, and reversed(xrange(6)) appears to do the right
thing too. I didn't know about __reversed__ before.
[[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]


Are you sure? I could easily be missing something, since it's easy
to be offbyone with this stuff, but when I try it I get:

Python 2.4.1 (#1, May 16 2005, 15:19:29)
[GCC 4.0.0 20050512 (Red Hat 4.0.0-5)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
range(5,-1,-1) [5, 4, 3, 2, 1, 0]


and (skipping the ascii art banner):

Hugs 98: Based on the Haskell 98 standard
Haskell 98 mode: Restart with command line option -98 to enable extensions
Type :? for help
Hugs.Base> [5,4..0]
[5,4,3,2,1,0]
Hugs.Base>

which is equivalent. (Of course, having to use 6 instead of 5 in
the range(...) version invites an offbyone error).
Jan 16 '06 #18
Xavier Morel <xa**********@masklinn.net> writes:
The only thing that bothers me about the initial proposal is that
there would not, in fact, be any "range object", but merely a
syntactic sugar for list/generator creation.
Well, it could create something like an xrange. Maybe that's preferable.
Not that I really mind it, but, well, syntactic sugar for the
purpose of syntactic sugar really doesn't bring much to the table.


I don't think this is a valid objection. Python is already full of
syntactic sugar like indentation-based block structure, infix
operators, statements with keyword-dependent syntax, etc. It's that
very sugar that attracts programmers to Python away from comparatively
sugarless languages like Scheme. Indeed, Python is considered by many
to be a sweet language to program in, and they mean that in a nice
way.

If you want, you can think of it as "flavor" rather than "sugar". We
aren't after syntactic minimalism or we'd be using Scheme. The
criterion for adding something like this to Python should be whether
makes the language taste better or not.
Jan 16 '06 #19
Thanks for your replies. So, some problems and possible solutions:

1) [f(n), f(n)-1 .. 0] can be easily catched by interpreter, and f(n)
can be evaluated only once.

2) if you need right border excluded, I think [0 .. n) is very clear
(and consistent with mathematics). And about brakets highlighting... Do
you _really_ need it in this expression? By the way, Python
editors/ide's can be easily extended to support this syntax, and even
to color brakets in [0 .. n) in different colors (so, it'll be very
clear if border is included or not).
But as for me, [0 .. n-1] is better because it looks more like ordinary
list, and we would have no problems on creating generators with
excluded border.

3) Of course, in some cases 'range' is more readable. As for your
examples:

[0,..9] versus range(10)
[55, ...73] versus range(55, 74)
[1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, 295] versus range(55, 296, 10)

a) you use "magic" 10 here...
[0 .. 10) or [0 .. 9] are IMO at least as nice as range(10). And what
about
[1 .. 10] and range(1, 11)? ;-)

b) [55 .. 73] vs range(55, 74)? IMO first is more readable. And you
actually can see what is the last value, while in "range" you think
something like: "if 74 is written here, then last value is 74-1 = 73"

c) [1, 3 .. len(mystr)] vs range(1, len(mystr)+1, 2). As for me, I
should think for 2-3 seconds before I understand what range(1,
len(mystr)+1, 2) stands for :-)
And [1, 3 .. len(mystr)] is _obvious_ for almost everybody.

d) why you wrote '296' in range? Maybe, because you are really
experienced programmer, and you can automatically transform 295 -> 296
(because you _know_ the behaviour of range function). But if your task
is simply to iterate through sequence like n1, n2, n3, ... n(X-1), nX,
then [n1 .. nX] looks more natural, isn't it?

4) Proposed syntax can be easily extended to support chars (or any
other enumeration). (Maybe, without _implied_ :-) step parameter):

['a' .. 'd'] -> ['a','b','c','d'] (let it be a list for consistency)
('x' .. 'z') -> generator that yields 'x', 'y' and 'z' :-)
('a' ..) -> generator that yields english alphabet

Conclusion:
- I don't want to remove 'range' function from use completely. Feel
free to use it ;-)
- The only idea behind my proposal is obviousness. As for me, it was
sometimes slightly difficult to explain to somebody not familiar with
Python what

for n in range(1,10,2):
bla-bla-bla

stands for. Arithmetic sequence IMHO isn't that kind of thing when you
need to know some
special function to use it (is everything OK with my english here?)

Jan 16 '06 #20
Steven D'Aprano wrote:
Python indexing deliberately goes to one-past-the-end counting for a
reason: it helps prevent off-by-one signpost errors. This syntax goes
against that decision, and adds one more thing to memorise about Python:
the end index is not included in the list, except for arithmetic
sequences, where it is, sometimes but not necessarily. In [5,6,10] the end
index 10 is included; in [5,7,10] it isn't.


1) both in [5,6,10] and [5,7,10] 10 is included ;-)
And as for [5,6 .. 10] and [5,7 .. 10]... First one should look
like [5 .. 10] I think. But you are right:
it looks like a problem that in one case 10 is included and in
other not. I should think about it.

2) I think there's nothing to memorise: in mathematics [1,2 .. 10]
includes 10, almost everybody knows
it.
The thing that newcomer should memorise is (IMHO) _not_so_obvious_
behaviour of 'range'. You
may laugh at me, if you want. But look at Cormen's pseudocode: "for
j=1 to n" includes 'n'. (And
indexing starts from 1, not from 0, but that's another story).
Also, for non-programmer including the
borders is obvious, not otherwise.

Jan 16 '06 #21
"Gregory Petrosyan" <gr***************@gmail.com> writes:
1) both in [5,6,10] and [5,7,10] 10 is included ;-)
And as for [5,6 .. 10] and [5,7 .. 10]... First one should look
like [5 .. 10] I think. But you are right:
it looks like a problem that in one case 10 is included and in
other not. I should think about it.


[5,7 .. 10] means [5,7,9,11,13,15, ... ] limited to x<=10, so that
means [5,7,9]. 10 is not included.
Jan 16 '06 #22
"Gregory Petrosyan" <gr***************@gmail.com> writes:
1) [f(n), f(n)-1 .. 0] can be easily catched by interpreter, and f(n)
can be evaluated only once.
I think it would be counterintuitive for the interpreter to do that.
If I type f(n) twice I expect it to be evaluated twice.
2) if you need right border excluded, I think [0 .. n) is very clear
(and consistent with mathematics).
Oh man, that's really ugly. I'm not crazy about Ruby's "..." either
though I guess it's ok. Using colon for non-inclusion might be more
Python-like since it resembles both the syntax and behavior of an
existing python range:

[0.. : n]

(hmm, it does look kind of ugly).
3) Of course, in some cases 'range' is more readable. As for your
examples:

[0,..9] versus range(10)
[55, ...73] versus range(55, 74)
[1, 3, ..len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, 295] versus range(55, 296, 10)
Those examples should be written:

[0 .. 9] versus range(10)
[55 .. 73] versus range(55,74)
[1, 3, .. len(mystr)] versus range(1, len(mystr)+1, 2)
[55, 65, .. 295] versus range(55, 296, 10)

I find the ".." version more readable than the "range" version for
all four cases. YMMV.
4) Proposed syntax can be easily extended to support chars (or any
other enumeration). (Maybe, without _implied_ :-) step parameter):

['a' .. 'd'] -> ['a','b','c','d'] (let it be a list for consistency)
Hmm:

Hugs.Base> ['a'..'d']
"abcd"
Hugs.Base>

Note that "abcd" in Haskell is actually a list of chars.
('a' ..) -> generator that yields english alphabet


I think this has to be an infinite generator or one that yields all
the ascii chars starting with 'a'.
Jan 16 '06 #23
Steven D'Aprano wrote:
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:
For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods)


No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some folks
want to undermine that.

*shakes head in amazement*

Steven, I never said that Python should use this syntax, I merely showed
how it was done in Ruby.

It's nothing more than a ... basis of discussion... not a "I want that
!!ONE" post (if I did, i'd be using Ruby and posting on c.l.r)

(and you didn't what by the way?)

Ok scratch that, you didn't notice the 3 periods.
Jan 17 '06 #24
Steven D'Aprano wrote:
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:
For those who'd need the (0..n-1) behavior, Ruby features something that
I find quite elegant (if not perfectly obvious at first), (first..last)
provides a range from first to last with both boundaries included, but
(first...last) (notice the 3 periods)


No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some folks
want to undermine that.

*shakes head in amazement*

Steven, I never said that Python should use this syntax, I merely showed
how it was done in Ruby.

It's nothing more than a ... basis of discussion... not a "I want that
!!ONE" post (if I did, i'd be using Ruby and posting on c.l.r)

(and you didn't what by the way?)
Jan 17 '06 #25
On Mon, 16 Jan 2006, it was written:
There's something to be said for that. Should ['a'..'z'] be a list or a
string?


And while we're there, what should ['aa'..'zyzzogeton'] be?

tom

--
Socialism - straight in the mainline!
Jan 17 '06 #26
On Mon, 16 Jan 2006, Gregory Petrosyan wrote:
Please visit http://www.python.org/peps/pep-0204.html first.

As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice.
Agreed. Although i have to say, i like the syntax there - it seems like a
really natural extension of existing syntax.
So, maybe there's a reason to adopt slightly modified Haskell's syntax?
Well, i do like the .. - 1..3 seems like a natural way to write a range.
I'd find 1...3 more natural, since an ellipsis has three dots, but it is
slightly more tedious.

The natural way to implement this would be to make .. a normal operator,
rather than magic, and add a __range__ special method to handle it. "a ..
b" would translate to "a.__range__(b)". I note that Roman Suzi proposed
this back in 2001, after PEP 204 was rejected. It's a pretty obvious
implementation, after all.
Something like

[1,3..10] --> [1,3,5,7,9]
(1,3..10) --> same values as above, but return generator instead of
list
[1..10] --> [1,2,3,4,5,6,7,8,9,10]
(1 ..) --> 'infinite' generator that yield 1,2,3 and so on
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on
-1. Personally, i find the approach of specifying the first two elements
*absolutely* *revolting*, and it would consistently be more awkward to use
than a start/step/stop style syntax. Come on, when do you know the first
two terms but not the step size?
1) "[]" means list, "()" means generator


Yuck. Yes, i know it's consistent with list comps and genexps, but yuck to
those too!

Instead, i'd like to see lazy lists used here - these look like lists, and
can be used exactly like a list, but if all you want to do is iterate over
them, they don't need to instantiate themselves in memory, so they're as
efficient as an iterator. The best of both worlds! I've written a sketch
of a generic lazy list:

http://urchin.earth.li/~twic/lazy.py

Note that this is what xrange does already (as i've just discovered).

tom

--
Socialism - straight in the mainline!
Jan 17 '06 #27
On Mon, 16 Jan 2006, Alex Martelli wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> wrote:
On Mon, 16 Jan 2006 12:51:58 +0100, Xavier Morel wrote:
For those who'd need the (0..n-1) behavior, Ruby features something
that I find quite elegant (if not perfectly obvious at first),
(first..last) provides a range from first to last with both boundaries
included, but (first...last) (notice the 3 periods)


No, no I didn't.

Sheesh, that just *screams* "Off By One Errors!!!". Python deliberately
uses a simple, consistent system of indexing from the start to one past
the end specifically to help prevent signpost errors, and now some
folks want to undermine that.

*shakes head in amazement*


Agreed. *IF* we truly needed an occasional "up to X *INCLUDED*"
sequence, it should be in a syntax that can't FAIL to be noticed, such
as range(X, endincluded=True).


How about first,,last? Harder to do by mistake, but pretty horrible in its
own way.

tom

--
Socialism - straight in the mainline!
Jan 17 '06 #28
Tom Anderson <tw**@urchin.earth.li> writes:
The natural way to implement this would be to make .. a normal
operator, rather than magic, and add a __range__ special method to
handle it. "a .. b" would translate to "a.__range__(b)". I note that
Roman Suzi proposed this back in 2001, after PEP 204 was
rejected. It's a pretty obvious implementation, after all.


Interesting, but what do you do about the "unary postfix" (1 ..)
infinite generator?
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on


-1. Personally, i find the approach of specifying the first two
elements *absolutely* *revolting*, and it would consistently be more
awkward to use than a start/step/stop style syntax. Come on, when do
you know the first two terms but not the step size?


Usually you know both, but showing the first two elements makes
sequence more visible. I certainly like (1,3..9) better than (1,9;2)
or whatever.
1) "[]" means list, "()" means generator

Yuck. Yes, i know it's consistent with list comps and genexps, but
yuck to those too!


I'd be ok with getting rid of [] and just having generators or
xrange-like class instances. If you want to coerce one of those to a
list, you'd say list((1..5)) instead of [1..5].
Jan 17 '06 #29
Dennis Lee Bieber <wl*****@ix.netcom.com> writes:
What would be expected from
[1, 3, 6 .. 20]
???
In Haskell:

Hugs.Base> [1,3,6..20]
ERROR - Syntax error in expression (unexpected `..')
Hugs.Base>
Again, I see [1, 3, 6, 7, 8, 9, 10, ... , 18, 19]


You'd write that in Haskell as 1:3:[6..19]. In Python I guess
you'd say [1,3]+[6..19].
Jan 17 '06 #30
Some ideas:

1) Let [a,b .. c] be *ordinary list* !
Just like [1,2,3]. Are there any questions why 3 is included in
[1,2,3]? IMO it's more correct to think about [first, next .. last] as
about syntax for list creation, but not as about
"syntax-to-replace-range-function". (And, because it's an ordinary
list, you could iterate through it in usual way: "for each element of
list do...")

2) [5 .. 0] -> [5,4,3,2,1,0]
So, if "next" is omited, let the default step be 1 if "first" < "last"
and -1 otherwise.

Jan 17 '06 #31
Op 2006-01-16, Alex Martelli schreef <al***@mail.comcast.net>:
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
> For finite sequences, your proposal adds nothing new to existing
> solutions like range and xrange.


Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).


But not easier than reversed(range(6)) [[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]


Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.

3) Allow slice notation to be used anywhere a value can be
used.

4) Provide a number of extra operators on slices.
__neg__ (reverses the slice)
__and__ gives the intersection of two slices
__or__ gives the union of two slices

5) Provide sequences with a range (or slice) method.
This would provide an iterator that iterates over
the indexes of the sequences. A slice could be
provided
for i in xrange(6):

would then become

for i in (0:6):

for a reversed sequence

for i in reversed(xrange(6)):

would become

for i in - (0:6):
for i, el in enumerate(sequence):

would become

for i in sequence.range():
el = sequence[i]

But the advantage is that this would still work when
someone subclasses a list so that it start index
is an other number but 0.

If you only wanted every other index one could do
the following

for i in sequence.range(::2):

which would be equivallent to

for i in sequence.range() & (::2):

--
Antoon Pardon
Jan 17 '06 #32
"Gregory Petrosyan" <gr***************@gmail.com> writes:
2) [5 .. 0] -> [5,4,3,2,1,0]
So, if "next" is omited, let the default step be 1 if "first" < "last"
and -1 otherwise.


So what do you want [a..b] to do? Dynamically decide what direction
to go? Ugh!
Jan 17 '06 #33
Hmm, and why not? Or you realy hate such behaviour?

Note, everything I post here is just some ideas I want to discuss, and
to make these ideas better after discussion. And this particular idea
needs to be discussed, too.

Jan 17 '06 #34
Antoon Pardon wrote:
Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.


Just checking your intent here. What should these do?

(2:5) # should give me 2, 3, 4
(2:5:-1) # infinite iterator 2, 1, 0, ...?
(:5) # start at -infinity? what does that even mean?
(:5:-1) # start at -infinity and go backwards?!!

I suspect you should be raising some sort of exception if the start
isn't defined.

STeVe
Jan 17 '06 #35
Hey guys, this proposal has already been rejected (it is the PEP 204).

Jan 17 '06 #36
On Tue, 16 Jan 2006, it was written:
Tom Anderson <tw**@urchin.earth.li> writes:
The natural way to implement this would be to make .. a normal
operator, rather than magic, and add a __range__ special method to
handle it. "a .. b" would translate to "a.__range__(b)". I note that
Roman Suzi proposed this back in 2001, after PEP 204 was rejected. It's
a pretty obvious implementation, after all.


Interesting, but what do you do about the "unary postfix" (1 ..)
infinite generator?


1.__range__(None)
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on


-1. Personally, i find the approach of specifying the first two
elements *absolutely* *revolting*, and it would consistently be more
awkward to use than a start/step/stop style syntax. Come on, when do
you know the first two terms but not the step size?


Usually you know both, but showing the first two elements makes sequence
more visible. I certainly like (1,3..9) better than (1,9;2) or
whatever.


I have to confess that i don't have a pretty three-argument syntax to
offer as an alternative to yours. But i'm afraid i still don't like yours.
:)
1) "[]" means list, "()" means generator

Yuck. Yes, i know it's consistent with list comps and genexps, but yuck
to those too!


I'd be ok with getting rid of [] and just having generators or
xrange-like class instances. If you want to coerce one of those to a
list, you'd say list((1..5)) instead of [1..5].


Sounds good. More generally, i'd be more than happy to get rid of list
comprehensions, letting people use list(genexp) instead. That would
obviously be a Py3k thing, though.

tom

--
Taking care of business
Jan 18 '06 #37
On Tue, 17 Jan 2006, Antoon Pardon wrote:
Op 2006-01-16, Alex Martelli schreef <al***@mail.comcast.net>:
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
For finite sequences, your proposal adds nothing new to existing
solutions like range and xrange.

Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).
But not easier than reversed(range(6)) [[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]


Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.

3) Allow slice notation to be used anywhere a value can be
used.

4) Provide a number of extra operators on slices.
__neg__ (reverses the slice)
__and__ gives the intersection of two slices
__or__ gives the union of two slices

5) Provide sequences with a range (or slice) method.
This would provide an iterator that iterates over
the indexes of the sequences. A slice could be
provided


+5
for i, el in enumerate(sequence):

would become

for i in sequence.range():
el = sequence[i]
That one, i'm not so happy with - i quite like enumerate; it communicates
intention very clearly. I believe enumerate is implemented with iterators,
meaning it's potentially more efficient than your approach, too. And since
enumerate works on iterators, which yours doesn't, you have to keep it
anyway. Still, both would be possible, and it's a matter of taste.
But the advantage is that this would still work when someone subclasses
a list so that it start index is an other number but 0.
It would be possible to patch enumerate to do the right thing in those
situations - it could look for a range method on the enumerand, and if it
found one, use it to generate the indices. Like this:

def enumerate(thing):
if (hasattr(thing, "range")):
indices = thing.range()
else:
indices = itertools.count()
return itertools.izip(indices, thing)
If you only wanted every other index one could do the following

for i in sequence.range(::2):

which would be equivallent to

for i in sequence.range() & (::2):


Oh, that is nice. Still, you could also extend enumerate to take a range
as an optional second parameter and do this with it. Six of one, half a
dozen of the other, i suppose.

tom

--
Taking care of business
Jan 18 '06 #38
Paul Rubin wrote:
I don't think this is a valid objection. Python is already full of
syntactic sugar like indentation-based block structure, infix
operators, statements with keyword-dependent syntax, etc. It's that
very sugar that attracts programmers to Python away from comparatively
sugarless languages like Scheme. Indeed, Python is considered by many
to be a sweet language to program in, and they mean that in a nice
way.

If you want, you can think of it as "flavor" rather than "sugar". We
aren't after syntactic minimalism or we'd be using Scheme. The
criterion for adding something like this to Python should be whether
makes the language taste better or not.


I don't know, most of the syntactic sugar I see in Python brings
something to the language, or trivialize the generation of structures
and constructs that may be complex or awkward without it, it has a
natural, honey-ish sweetness full of flavor, it does not taste like some
cancer-spawning artificial sweetener ;-)
Jan 18 '06 #39
Gregory Petrosyan wrote:
Hey guys, this proposal has already been rejected (it is the PEP 204).


No, this is a subtly different proposal. Antoon is proposing *slice*
literals, not *range* literals. Note that "confusion between ranges and
slice syntax" was one of the reasons for rejection of `PEP 204`_. Which
means that if Antoon really wants his proposal to go through, he
probably needs to make sure that slice literals are clearly distinct
from range literals.

... _PEP 204: http://www.python.org/peps/pep-0204.html

STeVe

Jan 18 '06 #40
Tom Anderson <tw**@urchin.earth.li> wrote:
...
Sounds good. More generally, i'd be more than happy to get rid of list
comprehensions, letting people use list(genexp) instead. That would
obviously be a Py3k thing, though.


I fully agree, but the BDFL has already (tentatively, I hope) Pronounced
that the [...] form will stay in Py3K as syntax sugar for list(...). I
find that to be a truly hateful prospect, but that's the prospect:-(.
Alex
Jan 18 '06 #41
Op 2006-01-17, Steven Bethard schreef <st************@gmail.com>:
Antoon Pardon wrote:
Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.
Just checking your intent here. What should these do?

(2:5) # should give me 2, 3, 4


Yes.
(2:5:-1) # infinite iterator 2, 1, 0, ...?
No it would give nothing, think xrange(2,5,-1)

(:5) # start at -infinity? what does that even mean?
I'm still thinking abouth this. My preffered idea now it
it would be an endless loop returning bottom elements.
(:5:-1) # start at -infinity and go backwards?!!
This would give nothing. The idea is that if the step is negative
the iteration stops when the (hidden) index is equal or smaller
than the stop value. This is true here from the start.
I suspect you should be raising some sort of exception if the start
isn't defined.


That is a possibility too.

--
Antoon Pardon
Jan 18 '06 #42
Op 2006-01-17, Gregory Petrosyan schreef <gr***************@gmail.com>:
Hey guys, this proposal has already been rejected (it is the PEP 204).


No it isn't. In my proposal [1, 2:8, 8] would be equivallent to
[1, slice(2,8), 8]. If someone would want the list [1,2,3,4,5,6,7,8]
with this notation, I would propose a flatten function witch would
work with iterators too, so that flat([1, 2:8, 8]) would be the
same as flat([1, range(2,8), 8])

In my view python already has slice literals. You are only limited
in using this literals an index in subscription. I think this is
a rathter pointless limitation and that literal slices could be
very usefull when made into literals and usable as parameters.

--
Antoon Pardon
Jan 18 '06 #43
Op 2006-01-18, Tom Anderson schreef <tw**@urchin.earth.li>:
On Tue, 17 Jan 2006, Antoon Pardon wrote:
Op 2006-01-16, Alex Martelli schreef <al***@mail.comcast.net>:
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:

Steven D'Aprano <st***@REMOVETHIScyber.com.au> writes:
> For finite sequences, your proposal adds nothing new to existing
> solutions like range and xrange.

Oh come on, [5,4,..0] is much easier to read than range(5,-1,-1).

But not easier than reversed(range(6)) [[the 5 in one of the two
expressions in your sentence has to be an offbyone;-)]]


Why don't we give slices more functionality and use them.
These are a number of ideas I had. (These are python3k ideas)

1) Make slices iterables. (No more need for (x)range)

2) Use a bottom and stop variable as default for the start and
stop attribute. top would be a value that is greater than
any other value, bottom would be a value smaller than any
other value.

3) Allow slice notation to be used anywhere a value can be
used.

4) Provide a number of extra operators on slices.
__neg__ (reverses the slice)
__and__ gives the intersection of two slices
__or__ gives the union of two slices

5) Provide sequences with a range (or slice) method.
This would provide an iterator that iterates over
the indexes of the sequences. A slice could be
provided


+5
for i, el in enumerate(sequence):

would become

for i in sequence.range():
el = sequence[i]


That one, i'm not so happy with - i quite like enumerate; it communicates
intention very clearly. I believe enumerate is implemented with iterators,
meaning it's potentially more efficient than your approach, too. And since
enumerate works on iterators, which yours doesn't, you have to keep it
anyway. Still, both would be possible, and it's a matter of taste.
But the advantage is that this would still work when someone subclasses
a list so that it start index is an other number but 0.


It would be possible to patch enumerate to do the right thing in those
situations - it could look for a range method on the enumerand, and if it
found one, use it to generate the indices. Like this:

def enumerate(thing):
if (hasattr(thing, "range")):
indices = thing.range()
else:
indices = itertools.count()
return itertools.izip(indices, thing)


Fine by me. I'm not against enumerate.
If you only wanted every other index one could do the following

for i in sequence.range(::2):

which would be equivallent to

for i in sequence.range() & (::2):


Oh, that is nice. Still, you could also extend enumerate to take a range
as an optional second parameter and do this with it. Six of one, half a
dozen of the other, i suppose.


Yes you could probably do so and I'm not against it, I just think that
range would be a better base on which you can build enumerate and other
things than that enumenrate can be a base. e.g. __len__ could be eliminated
and len would be defined as:

def len(seq):
rng = seq.range()
return rng.stop - rng.start
Jan 18 '06 #44
Op 2006-01-16, Gregory Petrosyan schreef <gr***************@gmail.com>:
Please visit http://www.python.org/peps/pep-0204.html first.

As you can see, PEP 204 was rejected, mostly because of not-so-obvious
syntax. But IMO the idea behind this pep is very nice. So, maybe
there's a reason to adopt slightly modified Haskell's syntax? Something
like

[1,3..10] --> [1,3,5,7,9]
(1,3..10) --> same values as above, but return generator instead of
list
[1..10] --> [1,2,3,4,5,6,7,8,9,10]
(1 ..) --> 'infinite' generator that yield 1,2,3 and so on
(-3,-5 ..) --> 'infinite' generator that yield -3,-5,-7 and so on

So,
1) "[]" means list, "()" means generator
2) the "start" is required, "step" and "end" are optional.

Also, this can be nicely integrated with enumerations (if they will
appear in python). Haskell is also example of such integration.


With some abuse of the language one can already do a number of things
in python2.4 now.

import sys
from types import SliceType

class vslice(object):

def __init__(self, fun):
self.fun = fun

def __getitem__(self, inx):
if not isinstance(inx, tuple):
inx = inx,
return self.fun(*inx)

@vslice
def rnglst(*arg):
lst = []
for el in arg:
if type(el) is SliceType:
start = el.start or 0
stop = el.stop or sys.maxint
step = el.step or 1
if step > 0:
while start < stop:
lst.append(start)
start += step
else:
while start > stop:
lst.append(start)
start += step
else:
lst.append(el)
return lst

rnglst[3,4,5] --> [3, 4, 5]
rnglst[1, 2:8] --> [1, 2, 3, 4, 5, 6, 7]
rnglst[3:9:2, 21:6:-3] --> [3, 5, 7, 21, 18, 15, 12, 9]

--
Antoon Pardon

Jan 18 '06 #45
Alex Martelli wrote:
Tom Anderson <tw**@urchin.earth.li> wrote:
Sounds good. More generally, i'd be more than happy to get rid of list
comprehensions, letting people use list(genexp) instead. That would
obviously be a Py3k thing, though.
I fully agree, but the BDFL has already (tentatively, I hope) Pronounced
that the [...] form will stay in Py3K as syntax sugar for list(...).


Do you have a link for that? I hadn't seen the pronouncement, and
http://wiki.python.org/moin/Python3%2e0 still lists that as an open issue.
I find that to be a truly hateful prospect


I'm not sure I find it truly hateful, but definitely unnecessary.
TOOWTDI and all...

STeVe
Jan 18 '06 #46
Steven Bethard <st************@gmail.com> writes:
I fully agree, but the BDFL has already (tentatively, I hope)
Pronounced
that the [...] form will stay in Py3K as syntax sugar for list(...).
> I find that to be a truly hateful prospect


I'm not sure I find it truly hateful, but definitely
unnecessary. TOOWTDI and all...


Well, [...] notation for regular lists (as opposed to list
comprehensions) is also unnecessary since we could use "list((a,b,c))".

Are we -really- after syntax minimalism? And if we are, shouldn't we
just all switch to using Scheme?
Jan 18 '06 #47
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
...
Well, [...] notation for regular lists (as opposed to list
comprehensions) is also unnecessary since we could use "list((a,b,c))".
Indeed, there's no reason list couldn't accept multiple arguments as an
alternative to one sequence argument, just like min and max. This would
make list(a, b, c) work just perfectly.
Are we -really- after syntax minimalism? And if we are, shouldn't we
just all switch to using Scheme?


No, we're not after syntax minimalism (which might justify moving to
Lisp or Scheme) but syntax that's simple and readable. I see no
credibly simple and readable alternative to {a:b, c:d} dictionary
display syntax, for example; dict(((a,b),(c,d))) just wouldn't cut it,
because of the "parentheses overload" (double entendre intended). But
list has no such problem, and there's really no added value of clarity
and readability in [a,b,c] vs list(a,b,c). And don't tell me that the
added value is that brackets "suggest lists", since they're used to
index tuples and dicts just as well;-). So the only reason they may
"suggest lists" is that you're used to that display form in Python, but
there's no real reason to HAVE a display form for lists at all, IMHO.
Alex
Jan 19 '06 #48
Alex Martelli wrote:
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
...
Well, [...] notation for regular lists (as opposed to list
comprehensions) is also unnecessary since we could use "list((a,b,c))".

[snip] ... or should that be list(snip)? But
list has no such problem, and there's really no added value of clarity
and readability in [a,b,c] vs list(a,b,c). And don't tell me that the
added value is that brackets "suggest lists", since they're used to
index tuples and dicts just as well;-). So the only reason they may
"suggest lists" is that you're used to that display form in Python, but
there's no real reason to HAVE a display form for lists at all, IMHO.

Alex


I'm reading this and thinking "I disagree (?!) with Alex Martelli;
[a,b,c] is so much easier to read and understand than list(a,b,c)"...

And then it dawned on me that, the first time I learned about list
comprehension (a little over a year ago, in A.M.'s Python in a
Nutshell), I had all kinds of trouble getting used to seeing right away
that the square brackets were an indication that a list was created.
At the time, I would have totally agreed that list(a,b,c) is *much*
clearer...
{or expressions like a = list(x for x in range(1)) ...}

*Now*, I prefer [], as I have been "trained" that way... but, thinking
back, and thinking about teaching Python, I'd have to agree with Alex's
point (not that anyone would care about *my* opinion ;-)

André

Jan 19 '06 #49
Steven Bethard wrote:
I'm not sure I find it truly hateful, but definitely unnecessary.
TOOWTDI and all...


TOOWTDI Considered Harmful.

There is confusion between "There's Only One Way To Do
It" and "There's One Obvious Way To Do It". The first
is pejorative, harmful if it were true but stupid since
it is never true. There is never only one way to do
anything but the most trivial things. Perhaps there is
only one way to do a null-op, (pass), but that's about it.

Even when people mean One Obvious and not Only One, it
is still harmful because the emphasis is wrong. The
emphasis is on the *restrictive nature* of a language
which merely gives one obvious way of doing things.

The motto from the Zen of Python is far more nuanced
and sensible, although not as snappy:

"There should be one-- and preferably only one
--obvious way to do it."

The emphasis in the Zen is about *enabling* Python to
have at least one (and preferably only one) obvious
ways to do things. The Zen is about making sure that
solutions are obvious in Python; TOOWTDI is about the
paucity of solutions in the language, and I cringe
every time I see it. See the difference?
--
Steven.

Jan 19 '06 #50

This discussion thread is closed

Replies have been disabled for this discussion.

By using this site, you agree to our Privacy Policy and Terms of Use.