4

从一个可迭代对象中,我想生成其前缀的可迭代对象(包括原始可迭代对象本身)。

for prefix in prefixes(range(5)):
    print(tuple(prefix))

应该导致

(0,)
(0, 1)
(0, 1, 2)
(0, 1, 2, 3)
(0, 1, 2, 3, 4)

或在

()
(0,)
(0, 1)
(0, 1, 2)
(0, 1, 2, 3)
(0, 1, 2, 3, 4)

for prefix in prefixes('Hello'):
    print(''.join(prefix))

应该导致

H
He
Hel
Hell
Hello

或在


H
He
Hel
Hell
Hello

(空前缀是否是结果的一部分对我来说并不重要,内部或外部结果迭代的确切类型也不重要。)

我能够设计出几种方法来实现这一点,但都觉得至少有点笨拙:

使用切片和 len:

(如果 iterable 是一个序列则有效)

def prefixes(seq):
    for i in range(len(seq)):
        yield seq[:i + 1]

或使用列表理解:

def prefixes(seq):
    return [seq[:i + 1] for i in range(len(seq))]

...或生成器表达式

def prefixes(seq):
    return (seq[:i + 1] for i in range(len(seq)))

(这些不会产生空前缀。要包含它,请在上述任何一个中替换[i + 1]为 just[i]range(len(seq))by range(len(seq) + 1)。)

这些感觉很笨重:

  • 因为它们不适用于所有类型的可迭代输入
  • 因为需要+ 1抵消
  • 调用rangelen事(虽然enumerate不会在这里变得更好)

使用连接

def prefixes(iterable):
    result = ()
    for elem in iterable:
        result += (elem,)
        yield result

(不包括空前缀。这可以通过在-loopresult之前产生一次来更改。)for

或使用itertools.accumulate

from itertools import accumulate as acc

def prefixes(iterable):
    return acc(iterable, lambda t, elem: t + (elem,), initial=())

或者更具可读性:

from itertools import accumulate

def _append(iterable, elem):
    return iterable + (elem,)

def prefixes(iterable):
    return accumulate(iterable, _append, initial=())

(这两个包括空前缀。如果不需要,请删除它。)

由于需要将元素打包到长度为一的容器中,以便将它们连接到现有的容器,因此这些感觉很笨重。

更优雅的解决方案?

我觉得我必须从itertools,中遗漏一些东西functoolsoperator或者more-itertools这将允许稍微甚至明显不那么笨重的实现。我的意思是,这与它非常相似more_itertools.powerset,只是它的一个非常具体的子集。

4

3 回答 3

2

类似于您的第一个串联示例,但构建一个列表而不是元组:

def prefixes(iterable):
    result = []
    for elem in iterable:
        result.append(elem)
        yield result

这消除了创建临时单元素元组的必要性。

于 2022-01-05T00:15:28.977 回答
2

以任何可行的通用方式编写函数,将其放入模块中,然后将其导入到需要它的代码中,这可能被认为是优雅的prefixes,因此它如何实现并不重要。

另一方面,需要额外的导入可能被认为不如简短的本地函数优雅,后者不太通用但更适合特定用例。

这是一种可能的非常通用的解决方案:

def prefixes(iterable):
    return itertools.accumulate(map(lambda x: (x,), iterable))

有理由认为它是优雅的:

  • 它使用标准库中已经提供的功能并实现主要目标,
  • 它没有明确提到accumulate已经隐含的连接,
  • 它不需要initial参数 to accumulate

但有些人发现使用mapandlambda不如for循环优雅。

于 2022-01-05T00:19:11.050 回答
0

这还没有完全充实,而且也有点笨拙:

def prefixes(iterable):
    from itertools import tee, islice
    iterator = iter(iterable)
    length = len(iterable)
    for slice_length, it in enumerate(tee(iterator, length), start=1):
        yield islice(it, slice_length)


for prefix in prefixes(range(5)):
    print(tuple(prefix))

for prefix in prefixes("Hello"):
    print("".join(prefix))

输出:

(0,)
(0, 1)
(0, 1, 2)
(0, 1, 2, 3)
(0, 1, 2, 3, 4)
H
He
Hel
Hell
Hello

您最终制作n+1了可迭代的独立迭代器。您还需要提前知道可迭代的长度,或者能够获取它的长度(因此您不能传入生成器。)

于 2022-01-05T00:11:35.767 回答