受this earlier stack overflow question的啓發我一直在考慮如何在保留每個迭代內元素順序的同時在python中隨機交錯迭代。例如:隨機交織多個迭代,同時保留它們在python中的順序
>>> def interleave(*iterables):
... "Return the source iterables randomly interleaved"
... <insert magic here>
>>> interleave(xrange(1, 5), xrange(5, 10), xrange(10, 15))
[1, 5, 10, 11, 2, 6, 3, 12, 4, 13, 7, 14, 8, 9]
原來的問題問隨機交錯兩個列表,a和b,以及接受的解決方案是:
>>> c = [x.pop(0) for x in random.sample([a]*len(a) + [b]*len(b), len(a)+len(b))]
然而,這種解決方案適用於只有兩個列表(儘管它可以很容易被擴展)並且依賴於a和b是列表這樣的事實,因此pop()
和len()
可以被調用,這意味着它不能用於迭代。它也有清空源列表a和b的不幸副作用。
爲原始問題提供的備選答案需要獲取源列表副本以避免修改它們,但這樣做會降低效率,尤其是源列表很大時。備用答案也使用len()
,因此不能僅用於迭代。
我寫我自己的解決方案,爲任意數量的輸入列表的工作,不對其進行修改:
def interleave(*args):
iters = [i for i, b in ((iter(a), a) for a in args) for _ in xrange(len(b))]
random.shuffle(iters)
return map(next, iters)
但這種解決方案還依賴於源參數是列表,以便len()
可以對它們使用。
那麼,有沒有一種有效的方法來在python中隨機交錯迭代,保留元素的原始順序,而不需要提前知道迭代的長度,並且不需要複製迭代?
編輯:請注意,與原始問題一樣,我不需要隨機化是公平的。
+1,儘管'_stop'的解決方案不太好。也許'嘗試:val = ...'\ n'除了StopIteration:iters.pop(i)'\ n'else:yield val'會更乾淨。 – glglgl
@glglgl:我一直在用各種方法來試驗發生器。我剛剛編輯成答案的版本是我最喜歡的版本。 – NPE
很好的答案。請注意,使用try-except比等效解決方案慢15%左右(沒有嘗試除外)(當我在CPython 2.7上嘗試時)。 – srgerg