Подтвердить что ты не робот

Python: возвращает индекс первого элемента списка, который делает переданную функцию true

Функция list.index(x) возвращает индекс в списке первого элемента, значение которого равно x.

Есть ли функция list_func_index(), похожая на функцию index() которая имеет функцию f() в качестве параметра. Функция f() запускается для каждого элемента e списка до тех пор, пока f(e) вернет True. Затем list_func_index() возвращает индекс e.

Codewise:

>>> def list_func_index(lst, func):
      for i in range(len(lst)):
        if func(lst[i]):
          return i
      raise ValueError('no element making func True')

>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
>>> list_func_index(l,is_odd)
3

Есть ли более элегантное решение? (и лучшее название для функции)

4b9b3361

Ответ 1

Вы можете сделать это в одной строке, используя генераторы:

next(i for i,v in enumerate(l) if is_odd(v))

Хорошая особенность генераторов в том, что они рассчитывают только до запрошенной суммы. Таким образом, запросить первые два индекса (почти) так же просто:

y = (i for i,v in enumerate(l) if is_odd(v))
x1 = next(y)
x2 = next(y)

Тем не менее, ожидайте исключение StopIteration после последнего индекса (так работают генераторы). Это также удобно в вашем подходе "взять первым", чтобы знать, что такого значения не найдено - функция list.index() выдаст здесь ValueError.

Ответ 2

Одной из возможностей является встроенная функция enumerate:

def index_of_first(lst, pred):
    for i,v in enumerate(lst):
        if pred(v):
            return i
    return None

Это типично для обозначения функции, подобной той, которую вы называете "предикатом"; он возвращает true или false для некоторого вопроса. Вот почему я называю это pred в моем примере.

Я также думаю, что было бы лучше вернуться None, так как это реальный ответ на вопрос. Вызывающий может выбрать взорваться на None, если требуется.

Ответ 3

@Принятый ответ лучше всего, но здесь немного бокового мышления, в основном для целей развлечения и обучения...:

>>> class X(object):
...   def __init__(self, pred): self.pred = pred
...   def __eq__(self, other): return self.pred(other)
... 
>>> l = [8,10,4,5,7]
>>> def is_odd(x): return x % 2 != 0
... 
>>> l.index(X(is_odd))
3

по существу, X цель состоит в том, чтобы изменить значение "равенства" от нормального до "удовлетворяет этому предикату", тем самым позволяя использовать предикаты во всех ситуациях, которые определяются как проверка равенства - для Например, он также позволит вам вместо кода if any(is_odd(x) for x in l): ввести код, короче if X(is_odd) in l: и т.д.

Стоит использовать? Не тогда, когда более явный подход, подобный принятому @Paul, так же удобен (особенно когда он изменился, чтобы использовать новую, блестящую встроенную функцию next, а не более старый, менее подходящий метод .next, как я предлагаю в комментарий к этому ответу), но есть и другие ситуации, когда это (или другие варианты идеи "изменить значение равенства" и, возможно, другие компараторы и/или хэширование) могут быть уместными. В основном, стоит знать об этой идее, чтобы избежать необходимости изобретать ее с нуля один раз; -).

Ответ 4

Не одна функция, но вы можете сделать это довольно легко:

>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z', 'x']
>>> map(test, data).index(True)
3
>>>

Если вы не хотите оценивать весь список сразу, вы можете использовать itertools, но это не так красиво:

>>> from itertools import imap, ifilter
>>> from operator import itemgetter
>>> test = lambda c: c == 'x'
>>> data = ['a', 'b', 'c', 'x', 'y', 'z']
>>> ifilter(itemgetter(1), enumerate(imap(test, data))).next()[0]
3
>>> 

Просто использование выражения генератора, вероятно, более читабельно, чем itertools.

Обратите внимание, что в Python3 map и filter возвращают ленивые итераторы, и вы можете просто использовать:

from operator import itemgetter
test = lambda c: c == 'x'
data = ['a', 'b', 'c', 'x', 'y', 'z']
next(filter(itemgetter(1), enumerate(map(test, data))))[0]  # 3

Ответ 5

Вариант ответа Алекса. Это позволяет не вводить X каждый раз, когда вы хотите использовать is_odd или любой предикат

>>> class X(object):
...     def __init__(self, pred): self.pred = pred
...     def __eq__(self, other): return self.pred(other)
... 
>>> L = [8,10,4,5,7]
>>> is_odd = X(lambda x: x%2 != 0)
>>> L.index(is_odd)
3
>>> less_than_six = X(lambda x: x<6)
>>> L.index(less_than_six)
2

Ответ 6

вы можете сделать это со списком:

l = [8,10,4,5,7]
filterl = [a for a in l if a % 2 != 0]

Затем filterl вернет все элементы списка, выполняющие выражение a% 2!= 0. Я бы сказал, более элегантный метод...