2015-06-23 65 views
12

想象一下,我有元組的順序列表:停止列表選擇?

s = [(0,-1), (1,0), (2,-1), (3,0), (4,0), (5,-1), (6,0), (7,-1)] 

給定參數X,我要選擇所有具有第一元素等於或大於X直到但不包括第一個元組的元組有-1作爲第二個元素。

例如,如果X = 3,我想在列表中選擇[(3,0), (4,0)]

我有一個想法是: 獲取截止鍵與

E = min (x [0] for x in s if (x [0] >= X) and (x [1] == -1)) 

然後選擇與X之間的鍵元素和E

R = [x for x in s if X <= x [0] < E] 

這給了我想要的R,但它似乎是真實的效率低下,涉及兩個表掃描。我可以在for循環中執行它,丟棄鍵太小的元組,並且當我點擊第一個阻塞元組時,打破。但是對於像列表選擇那樣的狗來說。

是否有超高效的python-esque(2.7)方法?

回答

26

你可以簡單地從列表中作爲發電機表達式過濾元組,然後你可以停止從發電機表達取值時,你得到的第一個元組,第二個元素是-1,這樣

>>> s = [(0,-1), (1,0), (2,-1), (3,0), (4,0), (5,-1), (6,0), (7,-1)] 
>>> from itertools import takewhile 
>>> X = 3 
>>> list(takewhile(lambda x: x[1] != -1, (item for item in s if item[0] >= X))) 
[(3, 0), (4, 0)] 

這裏,生成器表達式(item for item in s if item[0] >= X)將根據需要逐個給出值(它們不是一次生成的,因此我們在這裏節省內存),它們大於或等於X

然後,我們從該生成器表達式中取值,直到找到第二個元素不等於-1的元組爲止,其中itertools.takewhile

+9

你的答案優雅的滿足我的每一個問題。我高興地哭泣。 –

1

這裏有一個稍微哈克實施takewhile作爲發電機表達式的一部分:

def stop(): raise StopIteration 

result = (
    stop() if item[1] == -1 else 
    item 
    for item in s 
    if item[0] >= X 
) 

或以不同的措辭:

def until(cond): 
    if cond: raise StopIteration 
    return True 

result = (
    item for item in s 
    if item[0] >= X 
    and until(item[1] == -1) 
) 
+0

有趣的是,類似的方法在列表解析中不起作用,因爲'if'子句在理解/生成器內部代碼處理的'StopIteration'之外,所以異常會傳播。 (對於發生器也是如此,但它會被*使用的生成器的代碼自然處理)。這個評論是爲了那些試圖將這種技術應用於理解並獲得驚喜的人的潛在利益,就像我一樣。 :) – atzz