Python sentinel连续出现两次时停止迭代的简洁方法
我正在寻找一种方法来生成一个迭代器,该迭代器接受一个iterable,并只传递这些值,直到一个sentinel值直接连续出现两次。类似于国际热核实验堆(iter)(下一个哨兵)只有哨兵必须出现两次 下面这段相当没有创意的代码确实起到了作用,但肯定有一个不那么冗长的解决方案 因此,把它放在一个具体的问题上: 有没有一种方法可以避免使用完全成熟的生成器,并使用Python sentinel连续出现两次时停止迭代的简洁方法,python,iterator,generator,itertools,generator-expression,Python,Iterator,Generator,Itertools,Generator Expression,我正在寻找一种方法来生成一个迭代器,该迭代器接受一个iterable,并只传递这些值,直到一个sentinel值直接连续出现两次。类似于国际热核实验堆(iter)(下一个哨兵)只有哨兵必须出现两次 下面这段相当没有创意的代码确实起到了作用,但肯定有一个不那么冗长的解决方案 因此,把它放在一个具体的问题上: 有没有一种方法可以避免使用完全成熟的生成器,并使用itertools或生成器表达式实现同样的效果 >>> def repeat_offenders(a, sentinel):
itertools
或生成器表达式实现同样的效果
>>> def repeat_offenders(a, sentinel):
... ia = iter(a)
... for x in ia:
... if x==sentinel:
... try:
... y = next(ia)
... except StopIteration:
... yield x
... raise
... if y==sentinel:
... raise StopIteration
... yield x
... yield y
... else:
... yield x
以下是两个例子:
>>> ''.join(repeat_offenders('ABCABCAABBCC', 'B'))
'ABCABCAA'
>>> ''.join(repeat_offenders('ABABAB', 'B'))
'ABABAB'
注释类似,但缺少生成器角度。您可以根据
iwindow
定义repeat\u违犯者,滑动窗口配方(可用于任何iterable,而不仅仅是序列)和通常的iter(可调用,哨兵)
习惯用法:
import itertools as IT
def iwindow(iterable, n=2):
"""
Returns a sliding window (of width n) over data from the iterable.
s -> (s0,s1,...s[n-1]), (s1,s2,...,sn), ..., (sk, None, ..., None)
"""
iterables = IT.tee(iterable, n)
iterables = (IT.islice(it, pos, None) for pos, it in enumerate(iterables))
yield from IT.zip_longest(*iterables)
def repeat_offenders(iterable, sentinel, repeat=2):
return (item[0] for item in iter(iwindow(iterable, repeat).__next__,
(sentinel,)*repeat))
print(''.join(repeat_offenders('ABCABCAABBCC', 'B', 2)))
# ABCABCAA
print(''.join(repeat_offenders('ABABAB', 'B', 2)))
# ABABAB
iwindow
是itertools文档中所示内容的概括。通过使用iwindow
编写repeat\u
,我们可以将这个概念概括为在n
几乎免费重复之后停止。不确定是否有帮助,但我相信您的代码可以写得更简洁,如下所示:
from itertools import zip_longest
def repeat_offenders_jp(a, s):
for i, j in zip_longest(a, a[1:]):
if i == j and i == s:
break
else:
yield i
''.join(repeat_offenders_jp('ABCABCAABBCC', 'B')) # 'ABCABCAA'
''.join(repeat_offenders_jp('ABABAB', 'B')) # 'ABABAB'
此处使用zip\u longest
作为@jp\u data\u分析建议,但作为“一行”和takewhile
:
from itertools import zip_longest, takewhile
sentinel = 'B'
string = 'ABCABCAABBCC'
"".join(t[0] for t
in takewhile(lambda t: t[0] != sentinel or t[0] != t[1],
zip_longest(string, string[1:])))
# 'ABCABCAA'
string = 'ABABAB'
"".join(t[0] for t
in takewhile(lambda t: t[0] != sentinel or t[0] != t[1],
zip_longest(string, string[1:])))
# 'ABABAB'
Darkonaut,@jp_data_analysis,也许我没有说得足够清楚(好吧,我在问题中说iterable),但我正在寻找一些迭代证明的东西,这意味着直接索引、压缩自己等都不可用<代码>字符串
仅用于说明。@Paul Panzer字符串是可编辑的。否则你就不能在它们上面迭代。我想你想问一个迭代器,它接受一个迭代器?是的,字符串是iterables,但iterables不一定是字符串。如果某个东西对字符串有效,这并不意味着它对iterables一般有效。例如,有一些类似于文件对象的iterables没有\uuu getitem\uuuu
方法。提供一个我们无法使用的示例有什么意义?我们必须假设这就是你正在使用的iterable。我建议你用一个合适的例子提出一个新问题。每个例子都会有一些不能代表例子所代表的概念的特点。不依赖这些是常识问题。谢谢你的解决方案。它实际上比我简陋的生成器慢了一点(我想这应该归咎于tee
),但简单地推广到两次以上的重复是一个不错的奖励。正如你所说的,iwindow
是一件有用的事情。