python 中 reduce 函数的行为
behaviour of reduce function in python
我对 reduce
函数的行为感到困惑。
在第一个例子中,我得到了预期的结果:(1-1/2) * (1-1/3) = 1/3
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3])
0.33333333333333337
在第二个例子中,我没有得到预期的结果:(1-1/2) * (1-1/3) * (1-1/5) = 0.2666666
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3,5])
-1.5999999999999996
有人可以解释一下我缺少什么吗?
在您的第二个示例中,您有 3 个输入。您需要:
reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y)* (1 - 1.0/z),...
你需要的是 map
和 reduce
:
>>> 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 指出这一点(在评论中)。
你的函数出了什么问题
在这种情况下,实际上很容易看出什么不起作用,只需使用命名函数并添加一些 print
s:
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 列表的情况,但对于更多元素,它根本不应用您想要的公式。
备选
除了使用 map
和 reduce
,您还可以使用简单的 for
循环。让它们正确更容易,而且大多数时候它们更具可读性(在某些情况下它比 reduce
更快)。
prod = 1
for item in yourlist:
prod *= 1 - 1 / item
print(prod)
是的,现在是 4 行,而不是 1 行,但很容易理解发生了什么(但可能有一些边缘情况,其行为不像 reduce
,例如空输入)。
但我通常更喜欢简单的循环而不是复杂的 reduce
-操作,但一如既往的 YMMV。 :)
编辑:我同意上面的 map/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 作为我们的指南来计算它:
第一次迭代将是(将 2
和 3
的前 2 个迭代器值作为 x
和 y
):
(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
我对 reduce
函数的行为感到困惑。
在第一个例子中,我得到了预期的结果:(1-1/2) * (1-1/3) = 1/3
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3]) 0.33333333333333337
在第二个例子中,我没有得到预期的结果:(1-1/2) * (1-1/3) * (1-1/5) = 0.2666666
>>> reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y), [2,3,5]) -1.5999999999999996
有人可以解释一下我缺少什么吗?
在您的第二个示例中,您有 3 个输入。您需要:
reduce(lambda x, y: (1 - 1.0/x) * (1 - 1.0/y)* (1 - 1.0/z),...
你需要的是 map
和 reduce
:
>>> 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 指出这一点(在评论中)。
你的函数出了什么问题
在这种情况下,实际上很容易看出什么不起作用,只需使用命名函数并添加一些 print
s:
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 列表的情况,但对于更多元素,它根本不应用您想要的公式。
备选
除了使用 map
和 reduce
,您还可以使用简单的 for
循环。让它们正确更容易,而且大多数时候它们更具可读性(在某些情况下它比 reduce
更快)。
prod = 1
for item in yourlist:
prod *= 1 - 1 / item
print(prod)
是的,现在是 4 行,而不是 1 行,但很容易理解发生了什么(但可能有一些边缘情况,其行为不像 reduce
,例如空输入)。
但我通常更喜欢简单的循环而不是复杂的 reduce
-操作,但一如既往的 YMMV。 :)
编辑:我同意上面的 map/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 作为我们的指南来计算它:
第一次迭代将是(将 2
和 3
的前 2 个迭代器值作为 x
和 y
):
(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