28

I want to infinitely iterate through the list from 0 to the end, but in the next loop I want to start at 1 to the end plus 0, and the next loop would start at 2 to the end plus 0, 1, up to the last item where it would start again at 0 and go to the end.

Here is my code:

a = [ 0, 1, 2 ]
offset = 0
rotate = 0

while True:
    print(a[rotate])
    offset += 1
    rotate += 1
    if offset >= len(a):
        offset = 0
        rotate += 1
    if rotate >= len(a):
        rotate = 0

This is the solution I came up with so far. It's far from perfect.

The result that I want is:

0, 1, 2 # first iteration
1, 2, 0 # second iteration
2, 0, 1 # third iteration
0, 1, 2 # fourth iteration

and so on.

Scrampus
  • 461
  • 3
  • 7
  • 2
    I understand your question, I don't understand your code. It's not printing anything and not doing anything with `b`. How come you say it's a solution? (It's even giving `IndexError: list index out of range` for `b = a[rotate]`) – Tomerikoo Jun 19 '22 at 14:34
  • 2
    Kinda related: [Efficient way to rotate a list in python](https://stackoverflow.com/q/2150108/6045800) – Tomerikoo Jun 19 '22 at 14:39

5 Answers5

49

You can use a deque which has a built-in and efficient rotate function (~O(1)):

>>> d = deque([0,1,2])
>>> for _ in range(10):
...     print(*d)
...     d.rotate(-1)  # negative -> rotate to the left
...
0 1 2
1 2 0
2 0 1
0 1 2
1 2 0
2 0 1
0 1 2
1 2 0
2 0 1
0 1 2
wjandrea
  • 28,235
  • 9
  • 60
  • 81
GACy20
  • 949
  • 1
  • 6
  • 14
24

Try this:

a = [0, 1, 2]

while True:
    print(*a, sep=', ')
    a.append(a[0])
    a.pop(0)

Output:

0, 1, 2
1, 2, 0
2, 0, 1
0, 1, 2
1, 2, 0
2, 0, 1
...

Or, pop returns the element removed, so it can be simplified

a = [0, 1, 2]

while True:
    print(*a, sep=', ')
    a.append(a.pop(0))

[Thanks ShadowRanger and Tomerikoo for improvement suggestions.]

The Thonnu
  • 3,578
  • 2
  • 8
  • 30
  • 13
    Just for the record, `list.pop(0)` has O(n) time complexity, [copying and reallocating every outer iteration](https://stackoverflow.com/questions/195625/what-is-the-time-complexity-of-popping-elements-from-list-in-python). This makes it less good for large lists, but still fine for smallish lists (hundreds of elements) where the savings from having fewer Python statements is good, and the lists hopefully stay hot in cache. So you can get convenience without paying too high a performance penalty. It does still copy each element once for each time it's printed, though. – Peter Cordes Jun 19 '22 at 22:55
  • 1
    why would you modify the list when all the code needs to do is to print the elements?? – ilkkachu Jun 20 '22 at 07:05
  • 1
    @ilkkachu Because this is the easiest way... Look at all the other answers - do you see a more efficient one which doesn't modify the list? – The Thonnu Jun 20 '22 at 14:56
15

You can create the lists with offsets using list slicing, and then repeat them infinitely using itertools.cycle(). This computes all of the rotations exactly once, and then cycles through all of them:

from itertools import cycle, islice

lst = [0, 1, 2]
items = [lst[i:] + lst[:i] for i in range(len(lst))]
iterator = cycle(items)

for item in islice(iterator, 10):
    print(item)

The above approach is fast once you've gotten past the precomputation, but you may (depending on your use case) prefer an approach that does not have an upfront time/space cost. In that case, you can use a generator instead:

from itertools import cycle, islice

def rotate(lst):
    for offset in cycle(range(len(lst))):
        yield lst[offset:] + lst[:offset]

lst = [0, 1, 2]
for item in islice(rotate(lst), 10):
    print(item)

Both of these output:

[0, 1, 2]
[1, 2, 0]
[2, 0, 1]
[0, 1, 2]
[1, 2, 0]
[2, 0, 1]
[0, 1, 2]
[1, 2, 0]
[2, 0, 1]
[0, 1, 2]

These code snippets have been improved from a suggestion by wjandrea.

BrokenBenchmark
  • 18,126
  • 7
  • 21
  • 33
12

Here you have another alternative using pointers:

a = [ 0, 1, 2 ]
i = 0
l = len(a)
while True:
  out = []
  for j in range(i, i+l):
    out.append(a[j%l])
  print(out)
  i=(i+1)%l

Output:

[0, 1, 2]
[1, 2, 0]
[2, 0, 1]
[0, 1, 2]
[1, 2, 0]
[2, 0, 1]
Cardstdani
  • 4,999
  • 3
  • 12
  • 31
5

Another option, using list slicing:

cycles = [a[i:]+a[:i] for i, _ in enumerate(a)]
while True:
    for c in cycles: print(c)

Or, if you don't want to precalculate O(n^2) space for cycles, you can keep making the cycles afresh:

from itertools import count
n = len(a)
for i in count():
    j = i%n
    print(a[j:]+a[:j])
J.G.
  • 295
  • 2
  • 13