Traté de hacerlo lo más eficiente posible.
Utiliza un generador; Aquellos que no estén familiarizados con estas bestias deben consultar their documentation y yield expressions.
Básicamente crea un generador de valores de la subsecuencia que se puede restablecer enviándole un valor verdadero. Si el generador se reinicia, comienza a ceder nuevamente desde el comienzo de sub
.
Luego compara los valores sucesivos de sequence
con los rendimientos del generador, restableciendo el generador si no coinciden.
Cuando el generador se queda sin valores, es decir, llega al final de sub
sin reiniciar, eso significa que hemos encontrado nuestra coincidencia.
ya que funciona para cualquier secuencia, incluso se puede utilizar en cadenas, en cuyo caso se comporta de manera similar a str.find
, excepto que devuelve False
en lugar de -1
.
Como nota adicional: creo que el segundo valor de la tupla devuelta debería, de acuerdo con los estándares de Python, normalmente ser uno más alto. es decir, "string"[0:2] == "st"
. Pero la especificación dice lo contrario, así es como funciona esto.
Depende de si se trata de una rutina de propósito general o si se está implementando algún objetivo específico; en el último caso, podría ser mejor implementar una rutina de propósito general y luego envolverla en una función que modifique el valor de retorno para adaptarlo a la especificación.
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
Por lo que vale la pena, volviendo '[principio, al final + 1]' es más Pythonic ya que parece que una rebanada - '(extremo + 1) -start' da la longitud de lo que se encuentra . –
Esto parece un mal diseño: a veces la función devuelve un bool, a veces devuelve una lista. Eso hace que sea muy difícil de usar, ya que debe verificar el tipo de devolución antes de poder hacer algo con el resultado. En mi humilde opinión, una función llamada "contiene" solo debería devolver verdadero o falso. –
Es un poco triste que las listas no tengan la funcionalidad necesaria incorporada, pero las cadenas sí ('str.find'). –