因此,我有一個函式可以輸出范圍串列,例如 [range(1,5),range(8,13)]。我需要遍歷那些不在此串列范圍內的整數。例如,使用前面的串列,我希望能夠遍歷前面提到的串列中的 5-7。
我將使用的串列和范圍可能會相當大(即整數范圍從 0 到數百萬)。
有沒有辦法做到這一點而不將每個范圍轉換為數字串列并使用 if not in 陳述句?
uj5u.com熱心網友回復:
使用 Mark 的模板:
r = [range(1,5), range(8,13), range(20, 25), range(22, 27), range(30, 35)]
def missing(r):
stop = r[0].stop
for a in r:
yield from range(stop, a.start)
stop = max(stop, a.stop)
print(list(missing(r)))
輸出:
[5, 6, 7, 13, 14, 15, 16, 17, 18, 19, 27, 28, 29]
uj5u.com熱心網友回復:
您可以壓縮對并從范圍的停止和開始創建新范圍。如果有重疊,這將起作用range(20, 25), range(22, 27),但如果某些范圍完全包含在其他范圍(如range(10, 20), range(12, 15))中,則不會。如果后者是可能的,您將需要添加一些邏輯來忽略包含的范圍。
r = [range(1,5), range(8,13), range(20, 25), range(22, 27), range(30, 35)]
def missing(r):
g = (range(a.stop, b.start) for a, b in zip(r, r[1:]))
for interval in g:
yield from interval
list(missing(r))
# [5, 6, 7, 13, 14, 15, 16, 17, 18, 19, 27, 28, 29]
對于范圍完全重疊的更復雜的場景,您可以忽略包含的范圍,例如:
r = [range(1,5), range(2, 4), range(8,13), range(20, 25), range(22, 27), range(23, 25), range(30, 35)]
def missing(r):
current = r[0]
for next_range in r[1:]:
if next_range.stop < current.stop:
continue
yield from range(current.stop, next_range.start)
current = next_range
list(missing(r))
# [5, 6, 7, 13, 14, 15, 16, 17, 18, 19, 27, 28, 29]
這忽略了range(2, 4)包含在range(1, 5).
uj5u.com熱心網友回復:
itertools 模塊可以解決這個問題:
>>> from itertools import pairwise, chain
>>> excluded = [range(1,5), range(8,13), range(20, 30), range(34, 38)]
>>> included = [range(p.stop, q.start) for p, q in pairwise(excluded)]
>>> list(chain.from_iterable(included))
[5, 6, 7, 13, 14, 15, 16, 17, 18, 19, 30, 31, 32, 33]
為了便于閱讀,我把它寫成兩個單獨的步驟:
將排除范圍轉換為包含范圍的串列。包含范圍是從前一個排除范圍的停止點和下一個排除范圍的開始點構建的。
鏈接步驟將所有這些范圍組合到一個迭代器中。
如果需要,可以將這些步驟合并在一起以制作單行:
for i in chain.from_iterable(range(p.stop, q.start) for p, q in pairwise(excluded)):
print(i)
編輯
在評論中,OP 指出輸入可能比示例中顯示的更復雜。以下是如何通過對輸入進行排序和洗掉重疊范圍來準備資料:
from operator import attrgetter
excluded = [range(23, 25), range(1,6), range(8,16), range(2, 5), range(22, 27),
range(3, 4), range(20, 25), range(7, 15), range(30, 35)]
pairs = sorted(map(attrgetter('start', 'stop'), excluded))
non_overlapping = []
start = None
for pair in pairs:
if start is None:
start, stop = pair
continue
next_start, next_stop = pair
if next_start >= stop:
non_overlapping.append((start, stop))
start, stop = next_start, next_stop
continue
stop = max(stop, next_stop)
if start is not None:
non_overlapping.append((start, stop))
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/474654.html
下一篇:重復回圈多次
