Не то, чтобы это «путало место десяти и цифр», это то, что в первом матче место десяти не отличается, поэтому оно считается частью совпадающего префикса.
Для вашего варианта использования кажется довольно простым решением этой двусмысленности: просто сопоставьте все соседние пары и возьмите минимум. Как это:
def prefix(x, y):
comp = SequenceMatcher(None, x, y)
matches = comp.get_matching_blocks()
prefix_match = matches[0]
prefix_size = prefix_match[2]
return prefix_size
pairs = zip(files, files[1:])
matches = (prefix(x, y) for x, y in pairs)
prefixlen = min(matches)
prefix = files[0][:prefixlen]
prefix
функция довольно проста, за исключением одной вещи: я сделал это взять один кортеж из двух значений вместо двух аргументов, просто чтобы сделать его проще позвонить с map
. И я использовал [2]
вместо .size
, потому что есть раздражающая ошибка в 2.7 difflib
, где второй вызов get_matching_blocks
может вернуть tuple
вместо namedtuple
. Это не повлияет на код как есть, но если вы добавите некоторую отладку print
, она сломается.
Теперь pairs
список всех соседних пар имен, созданный zip
ping вместе names
и names[1:]
. (Если это не ясно, print(zip(names, names[1:])
. Если вы используете Python 3.x, вам нужно print(list(zip(names, names[1:]))
вместо этого, потому что zip
возвращает ленивый итератор вместо печати списка.)
Теперь мы просто хотим вызовите prefix
на каждую из пар и возьмите наименьшее значение, которое мы вернем. Это то, для чего min
. (Я передаю его generator expression, который может быть хитрым понятие в первом, но если вы просто думать о нем, как list comprehension, что не строит список, это довольно просто.)
Вы могли бы, очевидно, компактна этот на две или три линии в то же время оставляя его читаемым:
prefixlen = min(SequenceMatcher(None, x, y).get_matching_blocks()[0][2]
for x, y in zip(files, files[1:]))
prefix = files[0][:prefixlen]
Однако стоит учесть, что SequenceMatcher
вероятно, слишком много здесь.Он ищет самые длинные совпадения в любом месте, а не только самые длинные префиксные совпадения, что означает, что это по существу O (N^3) по длине строк, когда нужно только O (NM), где M - длина результат. Кроме того, не исключено, что может быть, скажем, суффикс, длиннее самого длинного префикса, поэтому он вернет неправильный результат.
Итак, почему бы просто не сделать это вручную?
def prefixes(name):
while name:
yield name
name = name[:-1]
def maxprefix(names):
first, names = names[0], names[1:]
for prefix in prefixes(first):
if all(name.startswith(prefix) for name in names):
return prefix
prefixes(first)
просто дает 'FilePrefix10.jpg'
, 'FilePrefix10.jp',
F'` 'FilePrefix10.j , etc. down to
'. Поэтому мы просто перебираем их, проверяя, является ли каждый из них также префиксом всех других имен и возвращает первый из них.
И вы можете сделать это даже быстрее, думая посимвольно вместо префикса префикс:
def maxprefix(names):
for i, letters in enumerate(zip(*names)):
if len(set(letters)) > 1:
return names[0][:i]
Здесь мы просто проверка является ли первый символ является одинаковым во всех имен, то будет ли второй символ одинаковым во всех именах и т. д. Как только мы найдем тот, где это не удается, префикс - все символы до этого (от любого имени).
zip
реорганизует список имен в список кортежей, где первый - это первый символ каждого имени, второй - второй символ каждого имени и т. Д. То есть, [('F', 'F', 'F', 'F'), ('i', 'i', 'i', 'i'), …]
.
enumerate
просто дает нам индекс вместе со значением. Итак, вместо получения ('F', 'F', 'F', 'F')
вы получаете 0, ('F, 'F', F', 'F')
. Нам нужен этот индекс для последнего шага.
Теперь, чтобы проверить, что ('F', 'F', 'F', 'F')
все одинаковые, я просто положил их в set
. Если они все одинаковые, набор будет иметь только один элемент: {'F'}
, затем {'i'}
и т. Д. Если это не так, у него будет несколько элементов - {'1', '2'}
- и вот как мы знаем, что мы прошли префикс ,
@ mh00h: Кого вы пытаетесь понять первым? И знаете ли вы, как работают «карта», «постижения», генераторы и «все»? – abarnert
Теперь я перевариваю все это. У вас есть много документации (я предпочел бы сделать это, чем сначала, чем сообщение здесь); медведь со мной, пока я перевариваю все это! Удивительный ответ. Многому научиться здесь. – mh00h
@ mh00h: Хорошо, круто. Я попытаюсь добавить некоторые объяснения к ответу - какой-то будущий искатель, приходящий на поиски той же проблемы, может быть не так старателен, как вы. Если вы найдете что-то непонятное, дайте мне знать, чтобы мы могли его улучшить. (Кроме того, если какой-либо из них на самом деле не работает, сообщите мне об этом, я несколько раз редактировал их после первоначальной записи и тестирования их ...) – abarnert