A question of particular interest about python for loops. Engineering programs often require values at previous or future indexes, such as:
for i in range(0,n):
value = 0.3*list[i-1] + 0.5*list[i] + 0.2*list[i+1]
etc...
However I rather like the nice clean python syntax:
for item in list:
#Do stuff with item in list
or for a list of 2d point data:
for [x,y] in list:
#Process x, y data
I like the concept of looping over a list without explicitly using an index to reference the items in the list. I was wondering if there was a clean way to grab the previous or next item without looping over the index (or without keeping track of the index independently)?
EDIT:
Thanks Andrew Jaffe (and by proxy Mark Byers) and gnibbler for the simple, extendable examples. I wasn't aware of the itertools or nwise modules till now. John Machin - thanks for the very complex example of what NOT to do. You put a lot of effort into this example, obviously the somewhat recursive algorithm I presented cannot produce a list with the same number of elements as the input list and it presents problems if not using explicit indexes. An algorithm like this would commonly occur in signal processing.
Here's a recipe, based on the itertools pairwise code, which does general n-wise grouping:
import itertools
def nwise(iterable, n=2):
"s->(s_0,s_1, ..., s_n), (s_1,s_2,..., s_n+1), ... "
ntup = itertools.tee(iterable, n)
for i, item in enumerate(ntup):
for ii in range(i):
next(item, None)
return itertools.izip(*ntup)
Which can be used thusly:
>>> import nwise
>>> ll = range(10)
>>> for tup in nwise.nwise(ll,3): print tup
...
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 5)
(4, 5, 6)
(5, 6, 7)
(6, 7, 8)
(7, 8, 9)
[Thanks to Mark Byers' answer for the idea]
To have access to an element and the next one you can use the pairwise recipe that is shown in the itertools documentation:
def pairwise(iterable):
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return izip(a, b)
This could be adapted to allow access to three neighbouring elements instead of just two.
>>> from itertools import islice, izip
>>> seq = range(10)
>>> for x,y,z in izip(*(islice(seq,i,None) for i in range(3))):
... print x,y,z
...
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
this can be trivially extended beyond 3 items.
If you need it to work with any iterable, Andrew's answer is suitable, or you can do it like this
>>> from itertools import izip, islice, tee
>>> seq=(x for x in range(10))
>>> for x,y,z in izip(*(islice(j,i,None) for i,j in enumerate(tee(seq,3)))):
... print x,y,z
...
0 1 2
1 2 3
2 3 4
3 4 5
4 5 6
5 6 7
6 7 8
7 8 9
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With