计算迭代器平均值的生成器

3

我有一个迭代器,用于计算质数。我想创建一个生成器,以质数迭代器作为输入参数,计算循环的质数的平均值:

from itertools import islice, tee

def only_primes(stream):
    try:
        while True:
            is_valid, value = next(stream)
            while not is_valid:
                is_valid, value = next(stream)
            yield value
    except StopIteration:
        return

def is_prime(n):
    if n < 2:
        return False, n
    elif n == 2:
        return True, n
    sqrt_n = int(n**0.5)+1
    return len([i for i in range(2, sqrt_n+1) if n % i == 0]) == 0, n

prime_iterator = only_primes(map(is_prime, range(100)))

def prime_av(stream):
"""Generator that yields average value of looped prime numbers"""
    n = 0
    stats = dict()
    stats['mean'] = 0
    try:
        while True:
            prime = next(stream)
            n += 1
            stats['mean'] *= n - 1
            stats['mean'] += prime
            stats['mean'] /= n
            yield stats
    except StopIteration:
        return

如果我同时循环遍历rawprime_av(stats)迭代器,只有最后一个平均值被打印出来。为什么?
raw, stats = tee(prime_iterator)    
list(islice(zip(raw, prime_av(stats)), 10))

输出:

[(2, {'mean': 12.9}),
 (3, {'mean': 12.9}),
 (5, {'mean': 12.9}),
 (7, {'mean': 12.9}),
 (11, {'mean': 12.9}),
 (13, {'mean': 12.9}),
 (17, {'mean': 12.9}),
 (19, {'mean': 12.9}),
 (23, {'mean': 12.9}),
 (29, {'mean': 12.9})]
2个回答

1
问题在于平均迭代器不断更改同一字典对象并产生它。 如果您在循环期间打印结果,那么结果就是您所期望的,但是如果将结果放入列表中(就像您正在做的那样),则最终列表将只包含对相同对象的引用,该对象当然也将具有最后计算出的平均值。
例如,将代码更改为:
def prime_av(stream):
    """Generator that yields average value of looped prime numbers"""
    n = 0
    S = 0
    try:
        while True:
            prime = next(stream)
            n += 1
            S *= n - 1
            S += prime
            S /= n
            yield {"mean": S}
    except StopIteration:
        return

在每次迭代中都会分配一个新的字典,因此它将按您的期望行事。


0

问题出在这里:

stats['mean'] = 0

在这里:

yield stats

你看到重复打印相同值,是因为你重复生成对同一字典的引用。这些引用都保存在一个列表中。然后你打印了这个列表。如果你想要看到每次更新时该字典的中间状态,那么每次更改时都打印它,而不是进行所有更新并打印它们。只需更改以下内容即可:

print(list(islice(zip(raw, prime_av(stats)), 10)))

变成这样:

for i in islice(zip(raw, prime_av(stats)), 10):
    print(*i)

如果你想要这些值的列表,你需要将它们添加到一个列表中,而不是通过更改单个值来重复更新,例如将代码从这个变成这样:
def prime_av(stream):
    """Generator that yields average value of looped prime numbers"""
    n = 0
    stats = dict()
    stats['mean'] = 0
    try:
        while True:
            prime = next(stream)
            n += 1
            stats['mean'] *= n - 1
            stats['mean'] += prime
            stats['mean'] /= n
            yield stats
    except StopIteration:
        return

转换为:

def prime_av(stream):
    """Generator that yields average value of looped prime numbers"""
    n = 0
    stats = dict()
    stats['mean'] = [0]
    try:
        while True:
            prime = next(stream)
            n += 1
            stats['mean'].append(stats['mean'][-1])
            stats['mean'][-1] *= n - 1
            stats['mean'][-1] += prime
            stats['mean'][-1] /= n
            yield stats
    except StopIteration:
        return

然后,当你这样做:

x = list(islice(zip(raw, prime_av(stats)), 10))

值的字典在x[1][1]中:

{'mean': [0, 2.0, 2.5, 3.3333333333333335, 4.25, 5.6, 6.833333333333333, 8.285714285714286, 9.625, 11.11111111111111, 12.9]}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接