2015-06-23 10 views
12

Wyobraź sobie, że mam listę zamówień krotek:Wybór listy zatrzymania?

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

Biorąc pod uwagę parametr X, chcę zaznaczyć wszystkie krotki, które mają pierwszy element równy lub większy niż X aż do, lecz nie licząc pierwszego krotki że ma -1 jako drugi element.

Na przykład, jeśli X = 3, chcę wybrać listę [(3,0), (4,0)]

jeden pomysł miałem to: Zdobądź klucz odcięcia z

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

Następnie wybierz elementy z kluczami między X i E:

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

To daje mi to, co chcę w R, ale wygląda na to, że jest prawdziwe nieefektywne, obejmujące dwa skany tabel. Mógłbym to zrobić w pętli for, odrzucając krotki z kluczami zbyt małymi i zrywając, kiedy uderzę w pierwszą blokującą krotkę. Ale do biegania jak pies w porównaniu do wyboru listy.

Czy istnieje super-wydajny, python-esque (2.7) sposób robienia tego?

Odpowiedz

26

Można po prostu filtrować krotki z listy jako wyrażenie generatora, a następnie można przerywać wartości z wyrażeniem generatora, gdy pojawi się pierwszy krotce Drugim elementem jest -1, jak to

>>> 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)] 

W tym przypadku wyrażenie generatora, (item for item in s if item[0] >= X), będzie dawało wartości jeden po drugim, na żądanie, (nie są one generowane wszystkie naraz, więc tutaj zapisujemy pamięć), które są większe lub równe X.

Następnie przyjmujemy wartości z tego wyrażenia generatora, tylko dopóki nie znajdziemy krotki, której drugi element nie jest równy -1, z itertools.takewhile.

+9

odpowiedź elegancko spełnia wszelkie moje obawy. Płaczę z radości. –

1

Oto nieco hacky wdrożyć takewhile jako część wyrażenia generatora:

def stop(): raise StopIteration 

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

Albo z innego frazowania:

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

Co ciekawe, podobne podejście nie zadziała w zrozumieniu list, ponieważ klauzula 'if' znajduje się poza obsługą' StopIteration' przez wewnętrzny kod interpretujący/generator, więc propaguje wyjątek. (Z generatorami też tak działa, ale przetwarza się go naturalnie za pomocą kodu, który * używa * generatora). Ta uwaga jest dla potencjalnych korzyści tych, którzy spróbowaliby zastosować tę technikę do zrozumienia i byli zaskoczeni, jak ja. :) – atzz