你什么时候会使用 reduce() 而不是 sum()?

When would you use reduce() instead of sum()?

我最近开始学习函数式编程,并在尝试计算 class 的测验平均分时想出了这个例子。

我想出的例子是:

scores = [90, 91, 92, 94, 95, 96, 97, 99, 100]

def add(num1, num2):
    '''returns the sum of the parameters'''
    return num1 + num2

import operator 

timeit reduce(add, scores) / len(scores)  #--> 1000000 loops, best of 3: 799 ns per loop

timeit sum(scores) / len(scores)  #--> 1000000 loops, best of 3: 207 ns per loop

timeit reduce(operator.add, scores) / len(scores) #--> 1000000 loops, best of 3: 485 ns per loop

在上面的例子中,使用高阶函数似乎慢了将近 4 倍。

所以我的问题是,什么时候是使用高阶函数的好时机,因为上面的例子显然不是?

当您需要对数据列表进行 任意 操作时,

reduce() 才有意义,而不是当您已经拥有一个不仅性能优于 reduce() 在小列表上,但 大大地 在大列表上优于它。

reduce() 为您提供了创建任意折叠的灵活性,但这种灵活性是以一些性能开销为代价的,尤其是在大多数基本功能结构被认为略微超出主流的语言中。

Python 是 "functional",因为它具有 first-class 函数,但它主要不是函数式语言。它提供了大量用于循环的迭代器,并具有使显式循环易于编写的各种语言特性,但并不专注于递归定义的列表操作(尽管它确实在有限程度上允许它们——缺乏 TCO阻止我直接在 Python 中解释我的 Erlang 或 Guile 代码,但确实让我可以灵活地做 benchmark competing approaches that adhere to similar interfaces).

之类的事情

代替总和?从来没有。

但是在通过自定义方法进行聚合时,reduce 调用是可行的方法。

例如 product 可以定义为:

product = lambda iterable: reduce(operator.mul, iterable)

另外 sum 在 C 中实现。

reducesum 做的事情截然不同。考虑一个问题,例如“我有一个嵌套字典......

d = {'foo': {'bar': {'baz': 'qux'}}}

我想获取与键列表关联的值:['foo', 'bar', 'baz']”。这 可以 调用 reduce(如果你我是一种函数式编程的人):

>>> reduce(lambda subdict, k: subdict[k], ['foo', 'bar', 'baz'], d)
'qux'

请注意,您不能使用 sum 执行此操作。碰巧求和是一个简单的例子来说明 reduce 发生了什么(因为你可以用括号写出来,而且大多数程序员都熟悉括号如何分组数学运算)。

撇开性能问题不谈,我不得不说:使用 sum() 完全没有错,而且在风格上你 应该 选择 sum()超过 reduce()reduce() 更通用,因此可用于编写其他归约,而不仅仅是求和。 sum() 是一种非常常见的归约,值得拥有自己的名称和定义。

如果您查看函数式编程语言,您会发现例如它们具有用于处理序列的大型通用实用函数库,例如 Haskell 的 Data.List or Scheme's SRFI-1。这些库中的 lot 函数可以根据其他函数来编写;例如,Haskell 中的 map 函数可以写成 foldr(类似于 reduce()):

map :: (a -> b) -> [a] -> [b]
map f = foldr go []
  where f a bs = f a : bs

但是没有人认为 foldr 从而使 map 变得不必要或需要避免。相反,更通用的操作,如 foldrreduce() 被视为 构建块 以构建更专业的函数,使程序更易于编写和理解。

reduce()sum()是同一个关系。 reduce() 是当您还没有 sum() 之类的功能时可以使用的构建块。