reduce() 函数存在什么问题?(涉及 IT 技术)

81

目前网络上有很多关于Python 3.0中reduce()函数的变化以及为什么应该将其删除的激烈讨论。我有些难以理解为什么会有这种情况,因为我觉得在各种情况下使用它都是相当合理的。如果人们的厌恶只是主观的,我无法想象会有如此多的人关心它。

那我错过了什么?reduce()函数的问题是什么?

5个回答

80

正如Guido在他的Python 3000中reduce()的命运文章中所说:

现在考虑reduce()。实际上这是我一直最讨厌的一个,因为除了涉及 + 或 * 的一些示例之外,几乎每次我看到使用非平凡函数参数的reduce()调用时,都需要拿起笔和纸来绘制出实际被馈送到该函数中的东西,然后才能理解reduce()应该做什么。因此,在我看来,reduce()的适用性基本上局限于关联运算符,在所有其他情况下,最好明确地编写累加循环。

函数式编程HOWTO文章中有一个极具困惑性的reduce()的优秀示例:

Quick, what's the following code doing?

total = reduce(lambda a, b: (0, a[1] + b[1]), items)[1]

You can figure it out, but it takes time to disentangle the expression to figure out what's going on. Using a short nested def statements makes things a little bit better:

def combine (a, b):
    return 0, a[1] + b[1]

total = reduce(combine, items)[1]

But it would be best of all if I had simply used a for loop:

total = 0
for a, b in items:
    total += b

Or the sum() built-in and a generator expression:

total = sum(b for a,b in items)

Many uses of reduce() are clearer when written as for loops.


4
那么这样可能会更容易:sum(a, b in 项目) - John Millikin
32
reduce代码与for循环不等同。这是等效的写法:total = reduce(lambda total, (a,b): total + b, items, 0) - Nathan Shively-Sanders
4
"reduce"函数可能会混淆代码的意图。因此,应该从语言中删除"reduce"函数。虽然我不确定是否有人确切地这么说过,但这是一个无关紧要的推论。 - Tony
2
理解了你的观点,但公正地说,在你的例子中加入lambda函数相当于在警察阵容中向一个人递上一件染血的衬衫。我不会争辩reduce()起初并不容易理解,但functools中的所有内容都是如此。随着我学习使用它们,我发现functools越来越有用。现在,如果你想要混淆视听的例子,请看看itertools()。我也正在学习喜欢它们,但第一次约会很艰难。 :-) - JS.
4
论点是“让我们移动 reduce,因为它可能用于编写不清晰的代码。” 嗯,**+-也可以写不清晰的代码。那我们是否应该将它们移到functools中? - Eric Duminil
显示剩余9条评论

39

reduce() 函数并没有被删除,而是被移动到了 functools 模块中。Guido 的想法是,除了像求和这样的简单情况之外,使用 reduce() 编写的代码通常在写为累积循环时更加清晰易懂。


36
啊,那个推理太可怕了 :( - TraumaPony
25
是吗?Python 的哲学大部分都关于编写清晰易懂的代码。通常调用 reduce() 函数时,我需要拿出铅笔并画出函数调用的图表。 - John Millikin
35
除非你能向我展示一个显著的性能提升(至少2倍),否则我每天都会选择“明显清晰”而不是“表达紧凑”。 - Kevin Little

9

有人担心它会鼓励一种模糊的编程风格,而这种效果可以通过更清晰的方法实现。

我个人并不反对使用reduce,有时候我也觉得它是一个有用的工具。


4

reduce的主要存在原因是为了避免使用累加器显式编写for循环。虽然Python有一些支持函数式风格的工具,但并不鼓励使用。如果你喜欢“真正”的而非“Python式”的函数式风格,请使用现代 Lisp(Clojure?)或Haskell。


这就像是告诉华裔美国人,如果他们不喜欢在美国的待遇,就回中国去一样。我们都在这里为了让一门广泛使用的语言变得更好。没有必要要求人们去使用另一种语言。大多数情况下,他们无法这样做,因为存在一些限制,比如软件包的可用性。 - episodeyang
1
@episodeyang 这个隐喻很奇怪,带有很多情感包袱,不适合用在技术讨论中。Python 并没有因为函数式编程范式变得“更好”(或更差),它只是根据自己的创建者的说法不是一个函数式语言。把 Lisp/Clojure/Haskell 的习惯用法硬套到 Python 这样的多范式、基本上是命令式、非函数式的语言中是没有问题的,但这并不意味着这种做法是正确的。 - ggorlen
对我来说,reduce是循环的一种特殊情况。它是一个输入为List[T],输出为T的循环。我认为上面的例子不太自然,因为循环的结果是不同的,但他们使用了一个技巧将其嵌入到T中(他们使用(0, acc)作为累加器)。我目前的用例是计算a ++ b ++ c ++ d ++ e,而无需创建T中的“零”,也无需混乱地处理“半个循环逻辑”(acc = loop[0]; for x in loop[1:]: acc = acc ++ x)。 - Att Righ

-2

使用reduce函数结合霍纳法则计算多项式值既紧凑又富有表现力。

在x处计算多项式值。 a是多项式的系数数组。

def poynomialValue(a,x):
   return reduce(lambda value, coef: value*x + coef, a)

1
这并不是真正试图回答楼主的问题。"为什么reduce是个问题?" 答案是:"这里有一个使用reduce的案例。" - ShadowRanger
一个强大的语言工具总是可以用来简洁地表达,但缺乏清晰度。我想举一个例子,展示reduce如何被用来紧凑地表达算法。请注意,这是一个不可结合的运算符的示例。 - user1153980
没有人争论reduce永远不能被清晰地使用,甚至也没有人说它不适用的例子很多。问题是它有什么问题,而不是它有什么优点。反对reduce的观点是它容易被误用,影响可读性,并且不符合Python的非函数式编程方向。这些是回答OP关于为什么将其降级到functools的可能答案。 - ggorlen

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