Python——如何循环遍历从随机点开始的范围

4
我有一个类别列表(1-4),我想要一个循环来遍历所有类别。但是,如果我的起始点在list[0]之前,我需要能够从随机类别开始并绕回到列表的开头。
我已经以相当冗长的方式做到了这一点,但我想知道是否有更快/更优雅的方法。这是我所做的(它可以工作):
def categorize(self, cat):

    cats = [1,2,3,4]
    if cat > 1: 
        ncats = cats[:(cat-1)]
        cats = cats[(cat-1):]
        cats.extend(ncats)

    for c in cats:
        pass
10个回答

8
from random import randrange
cats = [1,2,3,4]
i = randrange(len(cats))

for c in cats[i:]+cats[:i]:
   pass

根据建议将choice更改为randrange


+1,但是randrange(len(cats))比使用choice(range(len(cats)))更优雅。 - strcat
但是取模解决方案不是更有效吗?(它们不需要创建一个新列表 - 如果cats很大怎么办。) - spieden
我有一个类别列表(1-4)。您可以选择最易读的版本。 - YXD
起初我没有明确指出起始索引必须由另一个函数提供。但我喜欢你对循环语法的简化。在调用函数中,我实际上使用randrange()来生成循环的起始索引。 - monotasker

3

总体思路是:

>>> cats = [1, 2, 3, 4]
>>> import random
>>> r = random.randrange(len(cats))
>>> for i in range(len(cats)):
...     current = cats[(r+i)%len(cats)]
...     print current
...
3
4
1
2

3

好的,您可以将其简化为cats = cats[cat - 1:] + cats[:cat - 1]

或者构建一个自定义数据结构,覆盖迭代器(iter),使其从任意点开始循环一次。


对,我忘了可以用 + 代替 .extend()。 - monotasker

1

原始列表需要保持顺序吗?否则,您可以使用random.shuffle在原地随机化它:

cats = [1,2,3,4]
import random
random.shuffle(cats)
# Cats will now be in random order and can be looped normally.

抱歉,我在原问题中表述不够清楚。是的,列表必须保持有序,并且起始点(虽然是任意的)由另一个函数提供。 - monotasker

1
我建议使用通用的Warp/Round-Robin解决方案,具体如下:
from itertools import cycle:
from random import choice

cats = [1,2,3,4]
def warp(iterable, start):
    c = cycle(iterable)
    while c.next() is not start: pass
    for counter in range(len(iterable)):
        yield c.next()

#random start part:
for cat in warp(cats, choice(cats)):
    print cat

可迭代项的类型并不重要,您无需检查索引号,只需使用该项本身即可!


也许你的意思是“wrap”而不是“warp”? - Karl Knechtel
不要解释它。 - Don Question

0
from random import shuffle
shuffle(cats)
for c in cats:
    loop expressions here

这会破坏排序,不是吗? - spieden
抱歉,我在原问题中表述不够清楚。列表必须保持有序,并且起始点(虽然是任意的)由另一个函数提供。 - monotasker

0
>>> cats = [1,2,3,4]
>>> import random
>>> random.shuffle(cats)
>>> cats
[1, 3, 4, 2]
>>> random.shuffle(cats)
>>> cats
[1, 4, 3, 2]

[ 更新 ]

>>> def circle_iter(items, start=0):
...     l = len(items)
...     for i in items:
...         yield items[start]
...         start += 1
...         if start == l: start = 0
...
>>> cats = [ 1, 2, 3, 4 ]
>>> for cat in circle_iter(cats, 2): print cat
...
3
4
1
2
>>> for cat in circle_iter(cats, 1): print cat
...
2
3
4
1

抱歉,我在原问题中表述不够清楚。是的,列表必须保持有序,并且起始点(虽然是任意的)由另一个函数提供。 - monotasker

0
使用取模运算符!
import random

cats = [1,2,3,4]

i = random.randint(0,3)

for n in range(len(cats)):
    print cats[i%len(cats)]
    i+=1

0

这应该可以解决问题:

import random
cats = [1,2,3,4]
start = random.choice(cats)
new_order_cats = cats[cats.index(start):] + cats[:cats.index(start)]
print 'new order', new_order_cats

0
import random
cats = [1,2,3,4]

def cycle_cats(items, start):
    sorted_items = items[items[start]:] + items[:items[start]]
    for item in sorted_items:
        yield item

for cat in cycle_cats(cats, random.choice(cats)):
    print cat

保留顺序,不关心猫列表的长度,并期望调用者指定入口点。


@PauloScardine - 同意。我已经相应地编辑了我的答案。干杯! - D_Bye

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接