2

我正在寻找一种方法来生成一个迭代器,该迭代器接受一个可迭代的并只传递值,直到一个标记值直接连续出现两次。类似于iter(a.__next__, 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'

请注意,此问题类似,但缺少发电机角度。

4

3 回答 3

1

这里与zip_longest@jp_data_analysis 提出的一样,但作为“单线”并与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'
于 2018-02-05T19:17:11.643 回答
1

不确定它是否有帮助,但我相信您的代码可以更简洁地编写如下:

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'
于 2018-02-05T18:12:19.960 回答
1

你可以定义repeat_offenders一个iwindow滑动窗口配方(它可以在任何可迭代的,而不仅仅是序列上工作)和通常的iter(callable, sentinel)习惯用法:

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 文档中显示的pairwise配方的概括。通过写成repeat_offendersiwindow我们可以将这个概念推广到n几乎免费的重复后停止。

于 2018-02-05T18:11:21.860 回答