问题
Is it possible to create a python for-loop with a modulo operation? I have a ringbuffer in Python and I want to iterate the elements between the startPos
and endPos
indexes, where startPos
can have a bigger value than endPos
. In other programming languages, I would intuitively implement this with a modulo operator:
int startPos = 6;
int endPos = 2;
int ringBufferSize = 8;
for(int i = startPos, i != endPos, i = (i+1) % ringBufferSize) {
print buffer.getElementAt(i);
}
Is there a way to do this easily in Python? I only found the
for i in list:
print buffer[i]
Syntax but nothing which provides an equivalent solution to my problem.
My next approach would be to create the list in advance before iterate the indexes which are stored in the list. But is there a way to do this as a one-liner like in other programming languages by using the modulo operation directly in the for loop?
回答1:
You have some ways of doing that:
As you do in "other programing languages" (i.e. C derived syntaxes), just that you basically have to write their for
loop in a while form - and then you realize that C's for is just a while
nonetheless:
start_pos = 6
end_pos = 2
ring_buffer_size = 8
i = start_pos
while True:
i = (i + 1) % ring_buffer_size
if i <= end_pos:
break
# your code here
Now, for the for
statement, Python only has what is called "for each" - which always walks an iterable or sequence. So you can create an iterable that will yield your values -
def ring(start, end, buffer_size, increment=1):
i = start
while i != end:
yield i
i += 1
i %= buffer_size
for slot in ring(6, 2, 8):
# your code here
Note that while this second form is "bigger", it does abstract away your circular buffer logic, avoiding that hard code values get mixed with their meaning where you don't need to look at them - that is, inside the for
body itself.
Note that the actual idea of for
in Python is to iterate over the buffer contents itself, not and index that will lead to its contents.
So, the Python standard library includes a ready made circular buffer object already that always have its indexes normalized to 0 and (len - 1) -
just import deque
from the collections
module.
If you want a circular buffer with changing start and end indexes taht will wrap around and work automatically in for
statements, that is also relatively easy to do - if you don need the full functionality, just subclass list
, add the start
and end
indexes, and make a custom implementation of its __iter__
method:
class Circular(list):
def __init__(self, content, start, end):
super(Circular, self).__init__( content)
self.start = start
self.end = end
def __iter__(self):
for i in range(self.start, self.start + len(self)):
if i % len(self) == self.end: break
yield self[i % len(self)]
And now you can use this custom container in your code:
In [22]: mylist = Circular(range(8), 6 , 2)
In [23]: for i in mylist:
...: print(i)
...:
6
7
0
1
回答2:
For loops can take any iterable. As such you can create your own to do the work and drop it into the for loop. For example:
for i in [i % ring_buffer for i in range(start_pos, end_pos)]:
# Do stuff...
Or, to create an iterable directly:
for i in (i % ring_buffer for i in range(start_pos, end_pos)):
# Do stuff...
See the docs for more information about when you might want to create an iterator directly for this purpose.
回答3:
use range
for i in range(0,len(a)):
#code
i=i%x
回答4:
I believe you should be able to use while
instead of a for
loop. This should work as long as you just want to increment i
by 1 each time, then calculate the mod.
Try:
i = startPos
while (i <= endPos and i == (i+1) % ringBufferSize) :
print buffer.getElementAt(x)
i = i+1
来源:https://stackoverflow.com/questions/45839541/python-for-loop-with-modulo