Выбор остановочного списка?

Представьте, что у меня есть список заказов кортежей:

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, отбрасывая кортежи с слишком маленькими ключами и break, когда я нажимаю первый блокирующий кортеж. Но для бегов, как собака, по сравнению с выбором списка.

Есть ли супер-эффективный способ python-esque (2.7)?

Ответ 1

Вы можете просто фильтровать кортежи из списка в качестве выражения генератора, а затем вы можете прекратить принимать значения из выражения генератора, когда вы получите первый кортеж, второй элемент которого -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.

Ответ 2

Здесь немного взломано реализовать 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)
)