了解使用枚举减少列表列表

Understanding reduce with enumerate for a list of lists

我正在尝试将第一个列表的第一个数字 1 与第二个列表的第二个数字 5 相乘,依此类推以获得列表列表。例如,对于 [[1, 2, 3], [4, 5, 6], [7, 8, 9]],我想得到 1*5*9。

虽然有很多方法可以做到这一点,但我想知道 reduce 如何处理枚举:

def test(m):
    return reduce(lambda a, b: a[1][a[0]]*b[1][b[0]], enumerate(m))

print test([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

我会认为开头的a是(0,[1,2,3])所以a[1]是[1,2,3],a[0]是0,所以 a[1][a[0]] 是 1。

但是,我得到以下异常:

return reduce(lambda a, b: a[1][a[0]]*b[1][b[0]], enumerate(mat))
TypeError: 'int' object has no attribute '__getitem__'

为什么 a 是整数?

您的最终值和中间值是简单的整数。所以你应该从 1 开始,然后 lambda 将始终得到一个整数 a,即到目前为止的乘积。而 b 将是下一个枚举项。所以这是如何做到的:

>>> reduce(lambda a, b: a * b[1][b[0]],
           enumerate([[1, 2, 3], [4, 5, 6], [7,8,9]]), 1)
45

Python 2 仍然允许这样做,顺便说一句:

>>> reduce(lambda a, (i, b): a * b[i],
           enumerate([[1, 2, 3], [4, 5, 6], [7,8,9]]), 1)
45

因此,由于您试图获得对角线数字相乘的最终值,这就是 O 的做法:

示例:

def idiagonal(xs_of_ys):
    for i, x in enumerate(xs_of_ys):
        for j, y in enumerate(x):
            if i == j:
                yield y


print reduce(lambda x, y: x * y, idiagonal(xs), 1)  # prints 45

根据您在其中一条评论中的解释 - 您正在尝试乘以矩阵的对角线元素,现在我明白您为什么要 'enumerate' - 这确实是正确的,因为您想要得到那个指数。所以下面的代码会为你做到这一点。

首先 map 获取列表中的所有必需元素,然后 reduce 将它们乘以所需的值。注意:有几点需要注意,enumerate 会给你一个元组,所以你必须在 map lambda 中将它添加为 (x,y)。

a = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
reduce(lambda p,q: p*q, map(lambda (x,y): y[x], enumerate(a)), 1)

reduce(lambda p,q: p*q, [y[x] for (x,y) in enumerate(a)], 1)

编辑:添加了列表理解版本而不是 map-lambda。

请注意,列表理解版本与上述答案几乎一样快(只慢了大约 10%),为了可读性,我愿意换它。

尽管 Stefan 的回答在功能上是完美的,但我想指出...我们不是编译器,您知道。没有什么能阻止我们写一些更具可读性的东西,甚至可以在不解释任何事情的情况下说明你正在尝试做的事情:

from collections import namedtuple
Row = namedtuple('Row', ['num', 'columns'])

m = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
reduce(lambda result, row: result * row.columns[row.num], 
       (Row(*data) for data in enumerate(m)), 1)

查看 reduce 函数,现在我们知道您要在列号 = 行号的项目上累积。如果那不拼对角线,我不知道是什么:)