Python for loop with modulo

懵懂的女人 提交于 2020-06-26 07:15:12

问题


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 forstatements, 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!