【问题标题】:Python aggregate on a generator生成器上的 Python 聚合
【发布时间】:2014-05-07 22:14:25
【问题描述】:

我有一个生成器,它在每次迭代中返回一个列表。列表的每个元素可以是 0 或 1。我想计算返回的元素总数(包括 0 和 1)和返回的 1 的总数。我尝试使用这样的reduce函数来实现它:

t = reduce((lambda x,y:(y[0]+1,y[1]+x)), gen_fn(), (0,0)) 上面的

gen_fn() 是在每个 yield 语句中返回部分列表的生成器。我想通过使用元组(0,0)进行计数来实现它。鉴于从生成器返回的元素如下:

[0, 1, 1, 0, 1]

我对 t 的预期输出是 (5,3)。但是我的代码失败并显示此错误消息:

类型错误:+ 不支持的操作数类型:“int”和“tuple”

谁能帮我找出问题所在?我缺乏使用 reduce 和 lambda 函数的经验,这让我无法弄清楚我做错了什么。提前致谢。

【问题讨论】:

  • 我有点困惑。您的生成器是每次通过都产生一个值,还是一个值列表?也就是说,[0, 1, 1, 0, 1] 是单个值(例如 next(gen_fn()),还是整个值集(例如来自 list(gen_fn())?
  • @Blckknght 我错了; y 必须是列表,而不是整数,否则错误消息会有所不同。错误是针对y[1] + xx 是元组,所以 y 必须是整数的可索引容器。
  • 尽管如此; OP,你为什么期望输出是(5, 3)gen_fn() 是否只有 yield 一个值?
  • 有趣的是,我几乎说服@jonrsharpe 它返回列表,同时他几乎说服我它返回整数。似乎我们必须等待 OP 澄清,因为他的问题中的一个句子否定了另一个句子。
  • 上面的 gen_fn() 是在每个 yield 语句中返回部分列表的生成器 向我暗示它是多个列表,示例本质上只是 [[0, 1, 1, 0, 1]] - > (5,3).

标签: python lambda generator


【解决方案1】:

我认为这里最好的答案是保持简单:

count = 0
total = 0
for item in gen_fn():
    count += 1
    total += item

在此处使用 reduce() 只会降低代码的可读性。

如果您的问题是代码高尔夫,并且您想要一个单线(同时保持惰性评估),那么您想要:

count, total = collections.deque(zip(itertools.count(1), itertools.accumulate(gen_fn())), maxlen=1).pop()

当然,如果选择这样的结构而不是简单的解决方案,你会很生气。

编辑:

如果生成器产生多个较小的部分,则只需使用itertools.chain.from_iterable(gen_fn()) 将其展平即可。

【讨论】:

  • 如果可以的话,我会多次投票。我可以立即看到这段代码在做什么;没有理由混淆它。
【解决方案2】:

您的lambda 参数错误;第一个参数 (x) 是到目前为止的总数(元组),第二个参数 (y) 是新值(整数)。试试:

t = reduce((lambda x, y: (x[0]+1, x[1]+y)), gen_fn(), (0,0))

使用虚拟函数:

def gen_fn():
    for x in [0, 1, 1, 0, 1]:
        yield x

我收到(5, 3)

来自the docsreduce 的等效实现可能会让事情更清楚:

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        try:
            initializer = next(it)
        except StopIteration:
            raise TypeError('reduce() of empty sequence with no initial value')
    accum_value = initializer
    for x in it:
        accum_value = function(accum_value, x) # note value so far is first arg
    return accum_value

【讨论】:

    【解决方案3】:

    正如 jonrsharpe 所指出的,鉴于 reduce 的工作方式,您正在向后使用 lambda 参数。但是,如果从生成器生成的每个项目都是一个列表,那么如何添加内容可能会存在进一步的问题。

    这个问题是你的y 值(生成器产生的项目)不是一个数字,而是一个列表。您需要计算它的长度和 1s 的数量,因此您可能希望您的 lambda 函数为:

    lambda x, y: (x[0]+len(y), x[1]+sum(y))
    

    【讨论】:

      【解决方案4】:

      采取完全不同的方法怎么样?

      t = [(len(row), len(filter(lambda x: x == 1, row))) for row in gen_fn()]
      

      【讨论】:

      • 我不认为gen_fn 正在产生列表;请参阅我的答案中的虚拟实现
      • “我有一个生成器,它在每次迭代中返回一个列表。”我现在很困惑。 “上面的 gen_fn() 是返回部分列表的生成器。”列表的一部分是什么?
      • @jonrsharpe:我认为产生的值必须是列表,否则原始代码会在尝试索引 y 时出错,这是不可下标的。
      • @Blckknght 是的,当场;我只是在上面评论了很多!
      猜你喜欢
      • 1970-01-01
      • 2023-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-02
      • 2018-03-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多