slice notation as values?

A

Antoon Pardon

Op 2005-12-11 said:
Antoon said:
I also think that other functions could benefit. For instance suppose
you want to iterate over every second element in a list. Sure you
can use an extended slice or use some kind of while. But why not
extend enumerate to include an optional slice parameter, so you could
do it as follows:

for el in enumerate(lst,::2)

'Why not'? Because it makes for a more complicated interface for something
you can already do quite easily.

Do you think so? This IMO should provide (0,lst[0]), (2,lst[2]),
(4,lst[4]) ...

I haven't found a way to do this easily. Except for something like:

start = 0:
while start < len(lst):
yield start, lst[start]
start += 2

But if you accept this, then there was no need for enumerate in the
first place. So eager to learn something new, how do you do this
quite easily?
lst = ['ham','eggs','bacon','spam','foo','bar','baz']
list(enumerate(lst))[::2]
[(0, 'ham'), (2, 'bacon'), (4, 'foo'), (6, 'baz')]

No changes to the language necessary.
Or, without creating the full list intermediately,
lst = ['ham','eggs','bacon','spam','foo','bar','baz']
import itertools
list(itertools.islice(enumerate(lst), 0, None, 2))
[(0, 'ham'), (2, 'bacon'), (4, 'foo'), (6, 'baz')]

As far as I understand use of this idiom can turn an O(n)
algorithm into an O(n^2) algorithm.

Suppose I have a list with 10 000 elements and I want
the sum of the first 100, the sum of the second 100 ...

One way to do that would be:

for i in xrange(0,10000,100):
sum(itertools.islice(lst, i, i+100))

But itertools.islice would each time start from the begining
of the list and iterate over i elements before giving 100
elements to sum. Which would make this implementation O(n^2)
instead of O(n).
 
B

bonono

Antoon said:
Suppose I have a list with 10 000 elements and I want
the sum of the first 100, the sum of the second 100 ...

One way to do that would be:

for i in xrange(0,10000,100):
sum(itertools.islice(lst, i, i+100))

But itertools.islice would each time start from the begining
of the list and iterate over i elements before giving 100
elements to sum. Which would make this implementation O(n^2)
instead of O(n).
Can you use iter for this situation ?

a=iter(lst)
for i in xrange(0,10000,100):
sum(itertools.islice(a,100))
 
B

Bengt Richter

If you are willing to use square brackets, you can spell it
>>> for el in enoomerate[lst, ::2]: print el,

(see below ;-)
'Why not'? Because it makes for a more complicated interface for something
you can already do quite easily.

Do you think so? This IMO should provide (0,lst[0]), (2,lst[2]),
(4,lst[4]) ...

I haven't found a way to do this easily. Except for something like:

start = 0:
while start < len(lst):
yield start, lst[start]
start += 2

But if you accept this, then there was no need for enumerate in the
first place. So eager to learn something new, how do you do this
quite easily?
lst = ['ham','eggs','bacon','spam','foo','bar','baz']
list(enumerate(lst))[::2]
[(0, 'ham'), (2, 'bacon'), (4, 'foo'), (6, 'baz')]

It is not about what is needed, but about convenience.

Now let me see, in order to just iterate over the even elements
of a list with the index of the element, you turned an iterator
into a list, which you use to create an other list which you
will finaly iterate over.

If this is the proposed answer, I wonder why iterators were introduced
in the first place. I thought iterator were went to avoid the need
to construct and copy list when all you want is iterate and when
I ask how to get a specific iterator you come with a construct that
makes rather heavily use of list constructions.
Just for you ;-)
... def __getitem__(self, seq):
... if isinstance(seq, tuple):
... seq, slc = seq
... else:
... slc = slice(None)
... if not isinstance(slc, slice): slc = slice(None, slc)
... return itertools.islice(enumerate(seq), slc.start or 0, slc.stop, slc.step or 1)
...
>>> enoomerate = enoomerate()
>>>
>>> import string
>>> lst = list(string.ascii_lowercase) # legit list, though could use the string
>>> for el in enoomerate[lst, ::2]: print el,
...
(0, 'a') (2, 'c') (4, 'e') (6, 'g') (8, 'i') (10, 'k') (12, 'm') (14, 'o') (16, 'q') (18, 's') (
20, 'u') (22, 'w') (24, 'y')
>>> for el in enoomerate[lst, 3::3]: print el,
...
(3, 'd') (6, 'g') (9, 'j') (12, 'm') (15, 'p') (18, 's') (21, 'v') (24, 'y')
>>> for el in enoomerate[lst, 3]: print el,
...
(0, 'a') (1, 'b') (2, 'c')
>>> for el in enoomerate[lst, 3:6]: print el,
...
(3, 'd') (4, 'e') (5, 'f')
>>> for el in enoomerate[lst, 3:6:2]: print el,
...
(3, 'd') (5, 'f')

Regards,
Bengt Richter
 
A

Antoon Pardon

Op 2005-12-12 said:
Op 2005-12-10 said:
Antoon Pardon wrote:
[snip]
I also think that other functions could benefit. For instance suppose
you want to iterate over every second element in a list. Sure you
can use an extended slice or use some kind of while. But why not
extend enumerate to include an optional slice parameter, so you could
do it as follows:

for el in enumerate(lst,::2)

If you are willing to use square brackets, you can spell it

Hmm, I have to think about that.
for el in enoomerate[lst, ::2]: print el,

(see below ;-)
Just for you ;-)

Thank you.
... def __getitem__(self, seq):
... if isinstance(seq, tuple):
... seq, slc = seq
... else:
... slc = slice(None)
... if not isinstance(slc, slice): slc = slice(None, slc)
... return itertools.islice(enumerate(seq), slc.start or 0, slc.stop, slc.step or 1)
...
enoomerate = enoomerate()

import string
lst = list(string.ascii_lowercase) # legit list, though could use the string
for el in enoomerate[lst, ::2]: print el,

I am wondering a bit. Could this be turned into a decorator?

I don't have much experience with decorators, so I'll have to
think this through for a wgile. The idea would be a class
to be used as decorator that would turn a function returning
an iterator in a slicable iterator. Something like the following
maybe:

class SliceIterator(object):

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

def __getitem__(self, args):
if isinstance(args, tuple)
return self.func(*args)
else:
return self.func(args)

def __iter__(self):
return self.func(slice(None, None, None))

I could then write something like:

@SliceIterator
def srange(sl):
v = sl.stop
while v < sl.start:
yield v
v += sl.step


And use it as:

for i in srange[23:67:3]:
...

Hmm, I have to play with this a bit. Thanks for the idea.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,274
Messages
2,571,365
Members
48,050
Latest member
LucieDemps

Latest Threads

Top