Python 合并map()和reduce()

Python 合并map()和reduce(),不难发现基于一些简单的定义就可以创建高阶函数。下面介绍如何组合map()函数和reduce()函数生成map-reduce函数。

from typing import Callable, Iterable, Any
def map_reduce(
        map_fun: Callable,
        reduce_fun: Callable,
        source: Iterable) -> Any:
    return reduce(reduce_fun, map(map_fun, source))

这个由map()函数和reduce()函数组成的函数有3个参数:一个映射操作,一个归约操作,以及一个待处理的数据序列。

上面的定义非常宽泛,很难从中看出对数据类型的要求。由于映射和归约可能是非常复杂的操作,下面更严格地定义map-reduce函数:

from typing import Callable, Iterable, TypeVar
T_ = TypeVar("T_")
def map_reduce(
        map_fun: Callable[[T_], T_],
        reduce_fun: Callable[[T_, T_], T_],
        source: Iterable[T_]) -> T_:
    return reduce(reduce_fun, map(map_fun, source))

该定义多了几项约束。首先,可迭代对象需要包含类型一致的数据,我们把这个类型绑定为T_类型变量;然后,map()函数接收类型为T_的参数,并生成相同类型的结果;最后,归约函数接收两个T_类型的参数,返回一个同类型的参数。对于一个简单的数值计算应用来说,使用类型变量T_施加类型约束效果很好。

更多时候需要更严格地定义映射函数,例如Callable[[T1_], T2_]就体现了映射函数的核心特点:输入类型T1_和输出类型T2_可能是不同的。归约函数则需要保证输入和输出函数类型一致:Callable[[T2_, T2_], T2_]

可以分别提供映射函数和归约函数来得到计算平方和的归约定义,如下所示:

def sum2_mr(source: Iterable[float]) -> float:
    return map_reduce(
        lambda y: y ** 2, lambda x, y: x + y, source)

这里使用了lambda y: y ** 2作为平方计算的映射函数,归约部分使用lambda x, y: x + y作为参数。无须专门指定初始值,因为它就是平方函数映射后序列的第一个值。

上面的参数lambda x, y: x + y相当于+运算符,Python的operator模块中将所有算术运算符定义为短函数,下面用它简化上面的map-reduce定义:

from operator import add
def sum2_mr2(source: Iterable[float]) -> float:
    return map_reduce(lambda y: y ** 2, add, iterable)

这里使用了operator.add方法代替匿名函数来对数据进行求和。

计算可迭代对象中数值的个数如下所示:

def count_mr(source: Iterable[float]) -> float:
    return map_reduce(lambda y: 1, lambda x, y: x+y, source)

首先通过lambda y: 1将每个元素映射为1,再通过匿名函数或者operator.add做归约汇总,就得到了元素的个数。

总的说来,可以使用reduce()函数创建任何类型的归约,将大型数据集转换为单个数值。不过在使用reduce()函数时,需要注意一些限制。
避免像下面这样使用reduce()函数:

reduce(operator.add, list_of_strings, "")

该函数能运行,Python可以把add运算符应用于字符串集合,但使用"".join (list_of_strings)效率更高。通过timit测试发现使用reduce()组合字符串的时间复杂度是 O(n^2),且非常慢。在实际应用中,如果不仔细研究运算符处理复杂数据集的机制,很难发现效率的瓶颈在哪里。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程