Я пытался сделать это максимально эффективным.
Используется генератор; тем, кто не знаком с этими животными, рекомендуется проверить их документацию и документацию выражений доходности .
По сути, он создает генератор значений из подпоследовательности, который можно сбросить, отправив ему истинное значение. Если генератор сброшен, он снова начинает сдавать с начала sub
.
Затем он просто сравнивает последовательные значения sequence
с выходами генератора, сбрасывая генератор, если они не совпадают.
Когда в генераторе заканчиваются значения, то есть достигает конца sub
без сброса, это означает, что мы нашли наше совпадение.
Так как он работает для любой последовательности, вы даже можете использовать его для строк, в этом случае он ведет себя аналогично str.find
, за исключением того, что он возвращает False
вместо -1
.
В качестве дальнейшего примечания: я думаю, что второе значение возвращаемого кортежа должно, в соответствии со стандартами Python, обычно быть на единицу больше то есть "string"[0:2] == "st"
. Но в спецификации сказано иначе, вот как это работает.
Это зависит от того, предназначено ли это для рутины общего назначения или для достижения какой-то конкретной цели; в последнем случае может быть лучше реализовать процедуру общего назначения, а затем обернуть ее в функцию, которая переворачивает возвращаемое значение в соответствии со спецификацией.
def reiterator(sub):
"""Yield elements of a sequence, resetting if sent ``True``."""
it = iter(sub)
while True:
if (yield it.next()):
it = iter(sub)
def find_in_sequence(sub, sequence):
"""Find a subsequence in a sequence.
>>> find_in_sequence([2, 1], [-1, 0, 1, 2])
False
>>> find_in_sequence([-1, 1, 2], [-1, 0, 1, 2])
False
>>> find_in_sequence([0, 1, 2], [-1, 0, 1, 2])
(1, 3)
>>> find_in_sequence("subsequence",
... "This sequence contains a subsequence.")
(25, 35)
>>> find_in_sequence("subsequence", "This one doesn't.")
False
"""
start = None
sub_items = reiterator(sub)
sub_item = sub_items.next()
for index, item in enumerate(sequence):
if item == sub_item:
if start is None: start = index
else:
start = None
try:
sub_item = sub_items.send(start is None)
except StopIteration:
# If the subsequence is depleted, we win!
return (start, index)
return False