Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #19996 > unrolled thread

Cycle around a sequence

Started byMark Lawrence <breamoreboy@yahoo.co.uk>
First post2012-02-08 01:10 +0000
Last post2012-02-09 21:34 +1100
Articles 10 — 8 participants

Back to article view | Back to comp.lang.python


Contents

  Cycle around a sequence Mark Lawrence <breamoreboy@yahoo.co.uk> - 2012-02-08 01:10 +0000
    Re: Cycle around a sequence Christoph Hansen <ch@radamanthys.de> - 2012-02-08 03:01 +0100
    Re: Cycle around a sequence Neil Cerutti <neilc@norwich.edu> - 2012-02-08 14:25 +0000
      Re: Cycle around a sequence Terry Reedy <tjreedy@udel.edu> - 2012-02-08 15:15 -0500
      Re: Cycle around a sequence Mark Lawrence <breamoreboy@yahoo.co.uk> - 2012-02-08 22:47 +0000
      Re: Cycle around a sequence Serhiy Storchaka <storchaka@gmail.com> - 2012-02-09 12:10 +0200
    Re: Cycle around a sequence Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2012-02-09 03:55 +0000
      Re: Cycle around a sequence Chris Angelico <rosuav@gmail.com> - 2012-02-09 15:16 +1100
      Re: Cycle around a sequence Peter Otten <__peter__@web.de> - 2012-02-09 09:33 +0100
      Re: Cycle around a sequence Chris Angelico <rosuav@gmail.com> - 2012-02-09 21:34 +1100

#19996 — Cycle around a sequence

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2012-02-08 01:10 +0000
SubjectCycle around a sequence
Message-ID<mailman.5525.1328663401.27778.python-list@python.org>
I'm looking at a way of cycling around a sequence i.e. starting at some 
given location in the middle of a sequence and running to the end before 
coming back to the beginning and running to the start place.  About the 
best I could come up with is the following, any better ideas for some 
definition of better?

PythonWin 2.7.2 (default, Jun 12 2011, 15:08:59) [MSC v.1500 32 bit 
(Intel)] on win32.
Portions Copyright 1994-2008 Mark Hammond - see 'Help/About PythonWin' 
for further copyright information.
 >>> from itertools import chain
 >>> a=range(10)
 >>> g = chain((a[i] for i in xrange(4, 10, 1)), (a[i] for i in xrange(4)))
 >>> for x in g: print x,
...
4 5 6 7 8 9 0 1 2 3
 >>>
-- 
Cheers.

Mark Lawrence.

[toc] | [next] | [standalone]


#20002

FromChristoph Hansen <ch@radamanthys.de>
Date2012-02-08 03:01 +0100
Message-ID<jgsl21$q0d$1@online.de>
In reply to#19996
Mark Lawrence schrieb:
> I'm looking at a way of cycling around a sequence i.e. starting at some
> given location in the middle of a sequence and running to the end before
> coming back to the beginning and running to the start place.  About the
> best I could come up with is the following, any better ideas for some
> definition of better?

# quick&dirty

seq=range(10)
for x in seq[4:]+seq[:4]:
     print x

# or

def oneround(seq, start=0):
     i=start
     l=len(seq)
     while True:
         yield seq[i]
         i = (i+1) % l
         if i==start: break

for x in oneround(range(50), 4):
     print x




[toc] | [prev] | [next] | [standalone]


#20023

FromNeil Cerutti <neilc@norwich.edu>
Date2012-02-08 14:25 +0000
Message-ID<9pfeutFtjiU2@mid.individual.net>
In reply to#19996
On 2012-02-08, Mark Lawrence <breamoreboy@yahoo.co.uk> wrote:
> I'm looking at a way of cycling around a sequence i.e. starting
> at some given location in the middle of a sequence and running
> to the end before coming back to the beginning and running to
> the start place.  About the best I could come up with is the
> following, any better ideas for some definition of better?

Python's indices were designed for these kinds of shenanigans.

def rotated(seq, n):
    """Iterate through all of seq, but starting from index n.

    >>> ", ".join(str(n) for n in rotated(range(5), 3))
    '3, 4, 0, 1, 2'
    """

    i = n - len(seq)
    while i < n:
        yield seq[i]
        i += 1

if __name__ == "__main__":
    import doctest
    doctest.testmod()

If you have merely an iterable instead of a sequence, then look
to some of the other clever stuff already posted.

-- 
Neil Cerutti

[toc] | [prev] | [next] | [standalone]


#20032

FromTerry Reedy <tjreedy@udel.edu>
Date2012-02-08 15:15 -0500
Message-ID<mailman.5551.1328732195.27778.python-list@python.org>
In reply to#20023
On 2/8/2012 9:25 AM, Neil Cerutti wrote:
> On 2012-02-08, Mark Lawrence<breamoreboy@yahoo.co.uk>  wrote:
>> I'm looking at a way of cycling around a sequence i.e. starting
>> at some given location in the middle of a sequence and running
>> to the end before coming back to the beginning and running to
>> the start place.  About the best I could come up with is the
>> following, any better ideas for some definition of better?
>
> Python's indices were designed for these kinds of shenanigans.
>
> def rotated(seq, n):
>      """Iterate through all of seq, but starting from index n.
>
>      >>>  ", ".join(str(n) for n in rotated(range(5), 3))
>      '3, 4, 0, 1, 2'
>      """
>
>      i = n - len(seq)
>      while i<  n:
>          yield seq[i]
>          i += 1

This is really nice, in the category of "Why didn't I think of that?"
(Probably because I knew the % mod solution from C and never 'updated'!)

> if __name__ == "__main__":
>      import doctest
>      doctest.testmod()
>
> If you have merely an iterable instead of a sequence, then look
> to some of the other clever stuff already posted.

To make a repeating rotator is only a slight adjustment:

     k = n - len(seq)
     while True:
         i = k
         while i < n:
             yield seq[i]
             i += 1

-- 
Terry Jan Reedy

[toc] | [prev] | [next] | [standalone]


#20036

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2012-02-08 22:47 +0000
Message-ID<mailman.5554.1328741236.27778.python-list@python.org>
In reply to#20023
On 08/02/2012 14:25, Neil Cerutti wrote:
> On 2012-02-08, Mark Lawrence<breamoreboy@yahoo.co.uk>  wrote:
>> I'm looking at a way of cycling around a sequence i.e. starting
>> at some given location in the middle of a sequence and running
>> to the end before coming back to the beginning and running to
>> the start place.  About the best I could come up with is the
>> following, any better ideas for some definition of better?
>
> Python's indices were designed for these kinds of shenanigans.
>
> def rotated(seq, n):
>      """Iterate through all of seq, but starting from index n.
>
>      >>>  ", ".join(str(n) for n in rotated(range(5), 3))
>      '3, 4, 0, 1, 2'
>      """
>
>      i = n - len(seq)
>      while i<  n:
>          yield seq[i]
>          i += 1
>
> if __name__ == "__main__":
>      import doctest
>      doctest.testmod()
>
> If you have merely an iterable instead of a sequence, then look
> to some of the other clever stuff already posted.
>

The winner :)

-- 
Cheers.

Mark Lawrence.

[toc] | [prev] | [next] | [standalone]


#20062

FromSerhiy Storchaka <storchaka@gmail.com>
Date2012-02-09 12:10 +0200
Message-ID<mailman.5577.1328782266.27778.python-list@python.org>
In reply to#20023
08.02.12 22:15, Terry Reedy написав(ла):
> To make a repeating rotator is only a slight adjustment:
>
>     k = n - len(seq)
>     while True:
>         i = k
>         while i < n:
>             yield seq[i]
>             i += 1

     for i in range(n, len(seq)):
         yield seq[i]
     while True:
         for x in seq:
             yield x

[toc] | [prev] | [next] | [standalone]


#20044

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2012-02-09 03:55 +0000
Message-ID<4f3343b2$0$1615$c3e8da3$76491128@news.astraweb.com>
In reply to#19996
On Wed, 08 Feb 2012 01:10:28 +0000, Mark Lawrence wrote:

> I'm looking at a way of cycling around a sequence i.e. starting at some
> given location in the middle of a sequence and running to the end before
> coming back to the beginning and running to the start place.

If you have a sequence, and don't mind copying it, the easiest way is 
just to slice and join:


>>> a = range(20)
>>> b = a[5:] + a[:5]
>>> print b
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4]

Short, sweet, easy and simple. What's not to like about it?

For small (say, less than a few thousand of items) sequences, this 
probably is the fastest way to do it. 

Handling this lazily is trickier than it seems, because you have to store 
the first N items somewhere until you get to the rest of the iterable. 
There is no Right Way to do it, since the best solution will depend on 
how many items you have and how large N is.

Here's one way with itertools:


>>> from itertools import islice, chain, tee
>>> a = iter(range(20))
>>> t1, t2 = tee(a)
>>> b = chain(islice(t1, 5, None), islice(t2, None, 5))
>>> print list(b)
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4]

But read the docs for tee first: it may be that converting to a list is 
faster and more memory efficient.

http://docs.python.org/library/itertools.html#itertools.tee


Using tee may be overkill. Here's a simpler way:

>>> a = iter(range(20))
>>> t = list(islice(a, 5))
>>> b = chain(a, t)
>>> list(b)
[5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 0, 1, 2, 3, 4]

If your data is truly humongous, already stored in a list, and you don't 
want to make a copy, then I recommend your trick of generating the 
indexes:

def cycle(seq, n):
    for indexes in (xrange(n, len(seq)), xrange(n)):
        for i in indexes:
            yield seq[i]

If your data is humongous but only available lazily, buy more memory :)



-- 
Steven

[toc] | [prev] | [next] | [standalone]


#20048

FromChris Angelico <rosuav@gmail.com>
Date2012-02-09 15:16 +1100
Message-ID<mailman.5564.1328761006.27778.python-list@python.org>
In reply to#20044
On Thu, Feb 9, 2012 at 2:55 PM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> If your data is humongous but only available lazily, buy more memory :)

Or if you have a huge iterable and only need a small index into it,
snag those first few entries into a list, then yield everything else,
then yield the saved ones:

def cycle(seq,n):
	seq=iter(seq)
	lst=[next(seq) for i in range(n)]
	try:
		while True: yield next(seq)
	except StopIteration:
		for i in lst: yield i

>>> list(cycle(range(10,20),2))
[12, 13, 14, 15, 16, 17, 18, 19, 10, 11]

Requires storage space relative to n, regardless of the length of the iterator.

ChrisA

[toc] | [prev] | [next] | [standalone]


#20057

FromPeter Otten <__peter__@web.de>
Date2012-02-09 09:33 +0100
Message-ID<mailman.5573.1328776409.27778.python-list@python.org>
In reply to#20044
Chris Angelico wrote:

> On Thu, Feb 9, 2012 at 2:55 PM, Steven D'Aprano
> <steve+comp.lang.python@pearwood.info> wrote:
>> If your data is humongous but only available lazily, buy more memory :)
> 
> Or if you have a huge iterable and only need a small index into it,
> snag those first few entries into a list, then yield everything else,
> then yield the saved ones:

> def cycle(seq,n):
>         seq=iter(seq)
>         lst=[next(seq) for i in range(n)]
>         try:
>                 while True: yield next(seq)
>         except StopIteration:
>                 for i in lst: yield i

I think that should be spelt

def cycle2(seq, n):
    seq = iter(seq)
    head = [next(seq) for i in range(n)]
    for item in seq:
        yield item
    for item in head:
        yield item

or, if you are into itertools,

def cycle3(seq, n):
    seq = iter(seq)
    return chain(seq, list(islice(seq, n)))

$ python -m timeit -s'from tmp import cycle; data = range(1000); start=10' 
'for item in cycle(data, 10): pass'
1000 loops, best of 3: 358 usec per loop
$ python -m timeit -s'from tmp import cycle2; data = range(1000); start=10' 
'for item in cycle2(data, 10): pass'
1000 loops, best of 3: 172 usec per loop
$ python -m timeit -s'from tmp import cycle3; data = range(1000); start=10' 
'for item in cycle3(data, 10): pass'
10000 loops, best of 3: 56.5 usec per loop

For reference:

$ python -m timeit -s'data = range(1000); start=10' 'for item in 
data[start:] + data[:start]: pass'
10000 loops, best of 3: 56.4 usec per loop

[toc] | [prev] | [next] | [standalone]


#20064

FromChris Angelico <rosuav@gmail.com>
Date2012-02-09 21:34 +1100
Message-ID<mailman.5578.1328783688.27778.python-list@python.org>
In reply to#20044
On Thu, Feb 9, 2012 at 7:33 PM, Peter Otten <__peter__@web.de> wrote:
> Chris Angelico wrote:
>
>> def cycle(seq,n):
>>         seq=iter(seq)
>>         lst=[next(seq) for i in range(n)]
>>         try:
>>                 while True: yield next(seq)
>>         except StopIteration:
>>                 for i in lst: yield i
>
> I think that should be spelt
>
> def cycle2(seq, n):
>    seq = iter(seq)
>    head = [next(seq) for i in range(n)]
>    for item in seq:
>        yield item
>    for item in head:
>        yield item

Thanks, yeah, don't know what I was thinking :) Too much C work lately!

ChrisA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web