python 中 reduce 函数的行为

behaviour of reduce function in python

我对 reduce 函数的行为感到困惑。

有人可以解释一下我缺少什么吗?

在您的第二个示例中,您有 3 个输入。您需要:

reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y)* (1 - 1.0/z),...

你需要的是 mapreduce:

>>> from functools import reduce
>>> yourlist = [2, 3]
>>> reduce(lambda x, y: x*y, map(lambda x: (1-1/x), yourlist))
0.33333333333333337

>>> yourlist = [2, 3, 5]
>>> reduce(lambda x, y: x*y, map(lambda x: (1-1/x), yourlist))
0.2666666666666667

因为 map 将每个项目转换为 (1-1/item),然后 reduce 将所有项目相乘。

补充说明:

您也可以使用更快的 operator.mul 而不是 lambda x, y: x * y,例如:

>>> import operator
>>> yourlist = [2, 3, 5]
>>> reduce(operator.mul, map(lambda x: (1-1/x), yourlist))
0.2666666666666667

感谢@acushner 指出这一点(在评论中)。

你的函数出了什么问题

在这种情况下,实际上很容易看出什么不起作用,只需使用命名函数并添加一些 prints:

def myfunc(x, y):
    print('x = {}'.format(x))
    print('y = {}'.format(y))
    res = (1 - 1.0/x) * (1 - 1.0/y)
    print('res = {}'.format(res))
    return res

reduce(myfunc, [2, 3])
# x = 2
# y = 3
# res = 0.33333333333333337

reduce(myfunc, [2, 3, 5])
# x = 2
# y = 3
# res = 0.33333333333333337
# x = 0.33333333333333337
# y = 5
# res = -1.5999999999999996

所以它使用最后一个结果作为 "next" x 值。这就是为什么它适用于长度为 2 列表的情况,但对于更多元素,它根本不应用您想要的公式。

备选

除了使用 mapreduce,您还可以使用简单的 for 循环。让它们正确更容易,而且大多数时候它们更具可读性(在某些情况下它比 reduce 更快)。

prod = 1
for item in yourlist:
    prod *= 1 - 1 / item
print(prod)

是的,现在是 4 行,而不是 1 行,但很容易理解发生了什么(但可能有一些边缘情况,其行为不像 reduce,例如空输入)。

但我通常更喜欢简单的循环而不是复杂的 reduce-操作,但一如既往的 YMMV。 :)

编辑:我同意上面的 map/reduce 回答。

要了解原因,请阅读以下内容:

https://docs.python.org/2/library/functions.html#reduce

Reduce 递归 使用 2 个参数对列表的每个元素调用您的函数:一个 accumulator(上次调用的值)和当前元素.

所以你得到:

(1 - 1.0/( (1 - 1.0/2) * (1 - 1.0/3) )) * (1 - 1.0/5)

与:

reduce(lambda acc, x: (1 - 1.0/acc) * (1 - 1.0/x), [2,3,5])
>>-1.5999999999999996

为了补充为什么你得到 -1.5999999999999996 作为你的结果,为了完整性,我们可以使用 https://docs.python.org/2/library/functions.html#reduce 作为我们的指南来计算它:

第一次迭代将是(将 23 的前 2 个迭代器值作为 xy):

(1 - 1.0 / 2) * (1 - 1.0 / 3)

变成:

0.5 * 0.6666666666666667

产生:

0.33333333333333337.

然后我们使用 0.33333333333333337 进行下一次迭代,将此结果作为 x 并将 5 的下一次迭代编号作为 y:

因此,我们的第二次迭代将是:

(1 - 1.0 / 0.33333333333333337) * (1 - 1.0/5)

变成:

-1.9999999999999996 * 0.8

产生:

-1.5999999999999996