Я пытаюсь извлечь списки / подсписки из одного большего целочисленного списка с помощью Python2.7, используя начальные и конечные шаблоны. Я хотел бы сделать это с помощью функции, но не могу найти библиотеку, алгоритм или регулярное выражение для решения этой проблемы.
def myFunctionForSublists(data, startSequence, endSequence):
# ... todo
data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]
startSequence = [1,2,3]
endSequence = [4,5,6]
sublists = myFunctionForSublists(data, startSequence, endSequence)
print sublists[0] # [1, 2, 3, 99, 99, 99, 4, 5, 6]
print sublists[1] # [1, 2, 3, 99, 4, 5, 6]
Есть идеи, как я могу это реализовать?
@Prateek его ожидаемый результат показан тем, что он ожидает от операторов печати для печати
что такое startSequence? это значит похоже на startwith?
Если вы хотите сделать это с помощью регулярного выражения, вам нужно использовать String и не может применяться к списку Integer. Если вы хотите применить его к String, вы можете сделать это так: regex101.com/r/l1sx9V/1
Могут ли подсписки перекрываться?






Просто переберите все индексы в списке и сравните срез с startSequence или endSequence соответственно. Предполагая, что подсписки не должны перекрываться, вы можете использовать один и тот же итератор для обоих циклов.
def myFunctionForSublists(data, startSequence, endSequence):
positions = iter(range(len(data)))
for start in positions:
if data[start:start+len(startSequence)] == startSequence:
for end in positions:
if data[end:end+len(endSequence)] == endSequence:
yield data[start:end+len(endSequence)]
break
Таким образом, цикл start продолжится с того места, где остался цикл end. Если они может перекрываются, используйте два отдельных итератора для цикла, то есть for start in range(len(data)): и for end in range(start+1, len(data)):.
Используйте метод ниже:
def find_sub_list(sl,l):
sll=len(sl)
for ind in (i for i,e in enumerate(l) if e==sl[0]):
if l[ind:ind+sll]==sl:
return ind,ind+sll-1
find_sub_list([1,2,3], data)
>>>(2, 4)
find_sub_list([4,5,6], data)
>>>(8, 10)
data[2:10+1]
>>>[1, 2, 3, 99, 99, 99, 4, 5, 6]
Вы можете использовать аналогичный подход для sublists[1].
Предоставлено: найти-начальный-и-конечный-индексы-под-списка-в-списке
Вот решение O (n), которое находит совпадения, отслеживая совпадения шаблонов startSequence и endSequence.
def myFunctionForSublists(data, startSequence, endSequence):
start,end = tuple(startSequence), tuple(endSequence)
l1, l2 = len(start), len(end)
s = -1
result = []
for i,v in enumerate(zip(*[data[i:] for i in range(0,l1)])):
if v == start:
s = i
if v == end and s != -1:
result.append(data[s:i+l2])
s = -1
return result
print (myFunctionForSublists(data, startSequence, endSequence))
# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
Вот подход itertools, который использует collections.deque ограниченной длины для хранения в буфере последних элементов подходящего размера. Предполагается, что ваши подсписки не перекрываются и что начальная и конечная последовательности также не перекрываются.
Он работает для любой последовательности данных, начала, конца (даже для генераторов).
from collections import deque
from itertools import islice
def sublists(data, start, end):
it = iter(data)
start, end = deque(start), deque(end)
while True:
x = deque(islice(it, len(start)), len(start))
# move forward until start is found
while x != start:
x.append(next(it))
out = list(x)
x = deque(islice(it, len(end)), len(end))
# move forward until end is found, storing the sublist
while x != end:
out.append(x[0])
x.append(next(it))
out.extend(end)
yield out
data = [99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99]
startSequence = [1,2,3]
endSequence = [4,5,6]
print(list(sublists(data, startSequence, endSequence)))
# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
Вот более общее решение, которое не требует, чтобы списки были разрезаны, поэтому вы можете использовать его на других итерациях, таких как генераторы.
Мы сохраняем deque размером с последовательность start, пока не встретим его. Затем мы добавляем эти значения в список и продолжаем повторять последовательность. При этом мы сохраняем размер deque конечной последовательности, пока не увидим ее, а также добавляем элементы в список, который мы храним. Если мы сталкиваемся с конечной последовательностью, мы записываем yield в этот список и настраиваем deque на сканирование следующей начальной последовательности.
from collections import deque
def gen(l, start, stop):
start_deque = deque(start)
end_deque = deque(stop)
curr_deque = deque(maxlen=len(start))
it = iter(l)
for c in it:
curr_deque.append(c)
if curr_deque == start_deque:
potential = list(curr_deque)
curr_deque = deque(maxlen=len(stop))
for c in it:
potential.append(c)
curr_deque.append(c)
if curr_deque == end_deque:
yield potential
curr_deque = deque(maxlen=len(start))
break
print(list(gen([99, 99, 1, 2, 3, 99, 99, 99, 4, 5, 6, 99, 99, 1, 2, 3, 99, 4, 5, 6, 99], [1,2,3], [4,5,6])))
# [[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
Похоже, у нас была одна и та же идея, но нам удалось получить достаточно разные реализации!
Если вы действительно хотите использовать регулярные выражения, вы можете изменить списки целых чисел на строки и использовать регулярное выражение таким образом
import re
def find_span(numbers, start, end):
# Create strings from the start and end lists.
start_pattern = ''.join(map(chr, start))
end_pattern = ''.join(map(chr, end))
# convert the list to search into one string.
s = ''.join(map(chr, numbers))
# Create a pattern that starts and ends with the correct sublists,
# and match all sublists. Then convert each match back to a list of
# integers
# The '?' is to make the regex non-greedy
return [
[ord(c) for c in match]
for match in re.findall(rf'{start_pattern}.*?{end_pattern}', s, re.DOTALL)
]
>>> find_span(search, start, end) # Using OP's sample values
[[1, 2, 3, 99, 99, 99, 4, 5, 6], [1, 2, 3, 99, 4, 5, 6]]
Обратите внимание, что это не очень эффективно, поскольку требует динамического построения регулярного выражения при каждом его вызове. И вам нужно использовать re.DOTALL, потому что в противном случае он не будет соответствовать ничему, содержащему 10 (это кодировка ascii для новой строки). Однако, если вы используете действительно хочу использовать регулярные выражения, это сработает.
сначала используйте карту для преобразования int в str. Вы уверены, что он дает ожидаемый результат, опубликованный OP? Я пробовал регулярное выражение, но не могу избавиться от некоторых перекрытий
@mad_ Спасибо. Я понял, что забыл ?, который делает регулярное выражение нежадным. Это дает результат OP, который требуется с этим изменением.
не могли бы вы также опубликовать вывод? Просто хочу знать, что делаю не так. Был бы признателен. Спасибо
@mad_ обновил вывод. int также необходимо заменить на ord (забыл скопировать это изменение, когда обновил не жадный символ)
Каков ожидаемый результат ?