Here's a very efficient iterator (since you used the iterator
tag and said "iterate" multiple times). It uses only O(1) space and is very fast:
from itertools import islice, chain
def rotated(lst, start):
it = iter(lst)
next(islice(it, start, start), None)
return chain(it, islice(lst, start))
lst = list('abcdefgh')
start = 3
print(*rotated(lst, start))
Output (Try it online!):
d e f g h a b c
Benchmark with a list of a million numbers, showing tracemalloc
peaks and runtimes. Using the iterables suggested so far:
list_comprehension 8,697,776 bytes 1331.9 milliseconds
add_list_slices 16,000,000 bytes 28.4 milliseconds
iadd_list_slices 13,000,048 bytes 23.1 milliseconds
rotated_deque_1 8,243,328 bytes 26.9 milliseconds
rotated_deque_2 296 bytes 8.1 milliseconds
chained_list_slices 8,000,240 bytes 18.1 milliseconds
chained_iterators 360 bytes 11.6 milliseconds <-- mine
The first deque
solution is what the question asks for (starting with a list). The second is what ShadowRanger proposes (use a deque
from the get-go, instead of a list).
Code (Try it online!):
import tracemalloc as tm
from timeit import default_timer as timer
from collections import deque
from itertools import islice, chain
def list_comprehension(a, start):
return [a[(start + j) % len(a)] for j in range(len(a))]
def add_list_slices(a, start):
return a[start:] + a[:start]
def iadd_list_slices(a, start):
b = a[start:]
b += a[:start]
return b
def chained_list_slices(lst, start):
return chain(lst[start:], lst[:start])
def rotated_deque_1(lst, start):
d = deque(lst)
d.rotate(-start)
return d
def rotated_deque_2(lst, start):
deck.rotate(-start)
return deck
# deck.rotate(start) is done in the benchmark code, after the iteration
def chained_iterators(lst, start):
it = iter(lst)
next(islice(it, start, start), None)
return chain(it, islice(lst, start))
funcs = [
list_comprehension,
add_list_slices,
iadd_list_slices,
rotated_deque_1,
rotated_deque_2,
chained_list_slices,
chained_iterators,
]
a = list(range(10))
start = 3
deck = deque(a)
for func in funcs:
print(*func(a, start))
n = 10 ** 6
a = list(range(n))
start = n // 2
deck = deque(a)
for _ in range(3):
print()
for func in funcs:
tm.start()
t0 = timer()
deque(func(a, start), 0)
if func is rotated_deque_2:
deck.rotate(start)
t1 = timer()
print(f'{func.__name__:{max(len(func.__name__) for func in funcs)}} '
f'{tm.get_traced_memory()[1]:10,} bytes '
f'{(t1 - t0) * 1e3:6.1f} milliseconds')
tm.stop()