Чи є якісь вбудовані методи, які є частиною списків, які давали б мені перший і останній індекс якогось значення, наприклад:
verts.IndexOf(12.345)
verts.LastIndexOf(12.345)
Чи є якісь вбудовані методи, які є частиною списків, які давали б мені перший і останній індекс якогось значення, наприклад:
verts.IndexOf(12.345)
verts.LastIndexOf(12.345)
Відповіді:
Послідовності мають метод, index(value)
який повертає індекс першого входження - у вашому випадку це буде verts.index(value)
.
Ви можете запустити його, verts[::-1]
щоб дізнатися останній індекс. Ось, це було бlen(verts) - 1 - verts[::-1].index(value)
Якщо ви шукаєте індекс останнього входження myvalue
в mylist
:
len(mylist) - mylist[::-1].index(myvalue) - 1
ValueError
if, якщо myvalue
немає в mylist
.
Мабуть, два найефективніші способи знайти останній індекс:
def rindex(lst, value):
lst.reverse()
i = lst.index(value)
lst.reverse()
return len(lst) - i - 1
def rindex(lst, value):
return len(lst) - operator.indexOf(reversed(lst), value) - 1
Обидва вони займають лише додатковий простір O (1), і два розвороти на місці першого рішення набагато швидші, ніж створення зворотної копії. Давайте порівняємо це з іншими рішеннями, опублікованими раніше:
def rindex(lst, value):
return len(lst) - lst[::-1].index(value) - 1
def rindex(lst, value):
return len(lst) - next(i for i, val in enumerate(reversed(lst)) if val == value) - 1
Тестові результати, мої рішення - це червоний та зелений:
Це для пошуку числа у списку з мільйона чисел. Вісь х - для розташування шуканого елемента: 0% означає, що він знаходиться на початку списку, 100% означає, що він знаходиться в кінці списку. Усі рішення найшвидші за місцем розташування на 100%, причому два reversed
рішення майже не забирають на це часу, рішення з подвійним зворотом займає трохи часу, а зворотне копіювання займає багато часу.
Детальніший погляд на правий кінець:
У місці 100% рішення для зворотної копії та рішення для подвійного звороту витрачають весь свій час на розвороти ( index()
миттєво), тому ми бачимо, що два розвороти на місці приблизно в сім разів швидші, ніж створення зворотної копії.
Вище було з lst = list(range(1_000_000, 2_000_001))
, що в значній мірі створює об'єкти int послідовно в пам'яті, що надзвичайно зручно для кешу. Давайте зробимо це ще раз, перетасувавши список random.shuffle(lst)
(можливо, менш реалістичним, але цікавим):
Як і очікувалося, все стало набагато повільніше. Рішення зворотної копії страждає найбільше, на 100% це займає зараз приблизно в 32 рази (!) Стільки часу, скільки рішення подвійної зворотної копії. І enumerate
-рішення зараз є другим за швидкістю лише після розташування 98%.
Загалом, мені найбільше подобається operator.indexOf
рішення, оскільки воно є найшвидшим за останню половину або чверть усіх місць, які, мабуть, є найцікавішими, якщо ви насправді rindex
щось робите . І це лише трохи повільніше, ніж рішення з подвійним зворотом у попередніх місцях.
Усі тести виконані з CPython 3.9.0 64-bit 64-розрядної версії Windows 10 Pro 1903.
Списки Python мають index()
метод, за допомогою якого ви можете знайти позицію першого входження елемента у список. Зверніть увагу, що list.index()
підвищується, ValueError
коли елемента немає у списку, тому вам може знадобитися обернути його try
/ / except
:
try:
idx = lst.index(value)
except ValueError:
idx = None
Щоб ефективно знайти позицію останнього входження елемента в список (тобто без створення зворотного проміжного списку), ви можете використовувати цю функцію:
def rindex(lst, value):
for i, v in enumerate(reversed(lst)):
if v == value:
return len(lst) - i - 1 # return the index in the original list
return None
print(rindex([1, 2, 3], 3)) # 2
print(rindex([3, 2, 1, 3], 3)) # 3
print(rindex([3, 2, 1, 3], 4)) # None
Цей метод можна оптимізувати, ніж вище
def rindex(iterable, value):
try:
return len(iterable) - next(i for i, val in enumerate(reversed(iterable)) if val == value) - 1
except StopIteration:
raise ValueError
s.index(x[, i[, j]])
індекс першого входження x у s (при або після індексу i та перед індексом j)