Ответ 1
Я не уверен, как это pythonic
from itertools import takewhile,izip
x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
def allsame(x):
return len(set(x)) == 1
r = [i[0] for i in takewhile(allsame ,izip(*x))]
Данный: список списков, например [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
Todo: найдите самый длинный общий префикс всех подсписок.
Существует: в другом потоке Общие элементы между двумя списками, не использующими наборы в Python ", предлагается использовать" Counter ", который доступен выше python 2.7. Однако наш текущий проект был написан на python 2.6, поэтому" Counter" не используется.
В настоящее время я кодирую его так:
l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
newl = l[0]
if len(l)>1:
for li in l[1:]:
newl = [x for x in newl if x in li]
Но я считаю, что это не очень питонов, есть ли лучший способ кодирования?
Спасибо!
Новое редактирование. Извините, что в моем случае общие элементы списков в 'l' имеют одинаковый порядок и всегда начинаются с 0-го элемента. Таким образом, у вас не будет таких случаев, как [[1,2,5,6],[2,1,7]]
Я не уверен, как это pythonic
from itertools import takewhile,izip
x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
def allsame(x):
return len(set(x)) == 1
r = [i[0] for i in takewhile(allsame ,izip(*x))]
os.path.commonprefix()
хорошо работает для списков:)
>>> x = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> import os
>>> os.path.commonprefix(x)
[3, 2, 1]
Учитывая ваш пример кода, вам, похоже, нужна версия reduce(set.intersection, map(set, l))
, которая сохраняет начальный порядок первого списка.
Это требует алгоритмических улучшений, а не стилистических улучшений; "Питонический" код сам по себе не принесет вам пользы. Подумайте о ситуации, которая должна выполняться для всех значений, которые встречаются в каждом списке:
Учитывая список списков, значение появляется в каждом списке тогда и только тогда, когда оно встречается в списках
nlist
, гдеnlist
- общее количество списков.
Если мы можем гарантировать, что каждое значение происходит только один раз в каждом списке, то указанное выше можно перефразировать:
Учитывая список списков уникальных элементов, значение происходит в каждом списке тогда и только тогда, когда оно встречается
nlist
times total.
Мы можем использовать наборы, чтобы гарантировать, что элементы в наших списках уникальны, поэтому мы можем объединить этот последний принцип с простой стратегией счета:
>>> l = [[3,2,1], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> count = {}
>>> for i in itertools.chain.from_iterable(map(set, l)):
... count[i] = count.get(i, 0) + 1
...
Теперь нам нужно всего лишь фильтровать исходный список:
>>> [i for i in l[0] if count[i] == len(l)]
[3, 2, 1]
Здесь альтернативный способ использования itertools:
>>> import itertools
>>> L = [[3,2,1,4], [3,2,1,4,5], [3,2,1,8,9], [3,2,1,5,7,8,9]]
>>> common_prefix = []
>>> for i in itertools.izip(*L):
... if i.count(i[0]) == len(i):
... common_prefix.append(i[0])
... else:
... break
...
>>> common_prefix
[3, 2, 1]
Не уверен, как это можно было бы назвать "пифоническим".
Он неэффективен, так как он не срабатывает, как только обнаружено несоответствие, но его порядок:
([i for i,(j,k) in enumerate(zip(a,b)) if j!=k] or [0])[0]