У меня следующая проблема: я пытаюсь создать так называемые "биграммы", например:
Если у меня есть слово foobar
, я хочу получить список или генератор вроде: ["fo", "oo", "ob", "ba", "ar"]
. Идеальная функция для этого — more_itertools.windowed
. Проблема в том, что он возвращает кортежи, например:
In [1]: from more_itertools import windowed
In [2]: for i in windowed("foobar", 2):
...: print(i)
...:
('f', 'o')
('o', 'o')
('o', 'b')
('b', 'a')
('a', 'r')
Конечно, я знаю, что могу .join()
их, поэтому я бы:
In [3]: for i in windowed("foobar", 2):
...: print(''.join(i))
...:
...:
fo
oo
ob
ba
ar
Мне просто интересно, есть ли функция где-то в itertools
или more_itertools
, которую я не вижу, которая делает именно это. Или есть более «питоновский» способ сделать это вручную?
я не уверен, что вам нужно windowed
здесь, zip(s, s[1:])
работает так же хорошо. Один из способов избежать кортежей — [s[i:i+2] for i in range(len(s) - 1)]
Их также называют q-граммами длины 2. Я поклонник фильтрации q-грамм для поиска сходства на расстоянии редактирования.
Вы можете написать свою собственную версию widowed
, используя нарезку.
def str_widowed(s, n):
for i in range(len(s) - n + 1):
yield s[i:i+n]
Это гарантирует, что полученный тип совпадает с входным, но больше не принимает неиндексированные итерации.
more_itertools.windowed()
является питоническим. Рассмотрим pairwise()
itertools рецепт, который также дает кортежи:
def pairwise(iterable):
"s -> (s0, s1), (s1, s2), (s2, s3), ..."
a, b = tee(iterable)
next(b, None)
return zip(a, b)
Вы можете легко заменить windowed()
на pairwise()
и получить общие результаты — универсальное решение.
В качестве альтернативы вы можете разрезать строки, эмулируя попарный принцип сжатия повторяющихся, но смещенных строк:
Код
s = "foobar"
[a + b for a, b in zip(s, s[1:])]
# ['fo', 'oo', 'ob', 'ba', 'ar']
windowed
кажется мне довольно пифоническим.