美文网首页Pythoner集中营人工智能技术杂谈程序员
python学习笔记|函数式编程——map,filter,red

python学习笔记|函数式编程——map,filter,red

作者: KeyLiu7 | 来源:发表于2018-04-19 18:30 被阅读40次

    开始陆续学习python相关知识,这一篇介绍函数式编程相关知识。在文章中会介绍三个主要的高阶函数,从基本例子入手了解函数式编程。

    map()

    map 意思是映射,在数学的学习中,映射即为x与y存在的关系,即x通过一定的方法称为y。首先来看一个例子:将数组[1,2,3,4,5,6,7,8]中的元素均变为原来的2倍。

    乍一看很简单,通过一个简单的循环便可以解决。那么我们首先用这个方法解决:

    def num2(list):
        newList = []
        for x in list:
            x *= 2
            newList.append(x)
        return newList
    

    执行方法:

    list = [1,2,3,4,5,6,7,8]
    num2(list)
    

    输出结果为:

    [1,4,9,16,25,36,49,64]
    

    上述方法为整形,若将其扩展为字符或字符串呢,我们再来看一个例子——大小写转换。

    为了简单,我们只将['a','b','c','d']转换为['A','B','C','D']

    def upperNum(list):
        newList = []
        for x in list:
            newList.append(x[:].upper())
        return newList
    

    执行函数:

    list = ['a','b','c','d']
    print upperNum(list)
    

    输出结果为:

    ['A', 'B', 'C', 'D']
    

    通过上述两个例子,你是否发现了相同点。均是一个数组中元素通过某种方法进行变换,唯一不同的是传入数组的类型与方法不同。那么,我们能否定义出这个相同点函数,将数组和方法作为参数传递进去。

    幸运的是,泛型在python中非常简单,这让我们无需考虑数组中变量的类型。关于泛型的讨论,在下面补充中会介绍,如果你了解其他编程语言相信一定不会陌生。

    而如何将函数作为参数传递进去,这便是函数式编程

    函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

    python对函数式编程提供部分支持,既可以作为参数传递,又能返回一个函数。由于python允许使用变量,因此,python不是纯函数式编程语言

    我们来定义一个map函数,其参数为function方法和sequence数组,而通过研究发现其返回值依然是是一个数组。

    def map(function,sequence):
        newList = []
        for x in sequence:
            newList.append(function(x))
        return newList
    

    上述两个通过map方法便能合二为一:

    def num2(x):
      return x * x
    
    def upperNum(x):
        return x[:].upper()
    
    map(num2,list)
    map(upperNum,list)
    

    执行结果仍然为:

    [1,4,9,16,25,36,49,64]
    ['A', 'B', 'C', 'D']
    

    注,在python2中是能够直接出来结果的,但是在python3中这个会返回一个对象。要想用到结果就必须的在前面加上list来转化一下,比如:

    在系统库中的map函数定义如下,有一个可选参数

    map(function, sequence, *sequence_1)
    

    如果给定多个序列,则函数被调用,其中包含相应的参数列表每个序列的项;当不是全部时,用None代替缺失值使得序列的长度相同。如果函数没有,返回一个列表序列的项(或一个数组的列表,如果不止一个序列)。

    例如:

     l1 = [ 0, 1, 2, 3, 4, 5, 6 ]  
     l2 = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ]  
     map(f, l1, l2)  
    

    结果为:

    [(0, 'Sun'), (1, 'Mon'), (2, 'Tue'), (3, 'Wed'), (4, 'Thu'), (5, 'Fri'), (6, 'Sat')]  
    

    补充中提到一个匿名函数,对map方法仍能进行进一步的简化。

    filter()

    filter 意思是过滤器,即为筛选出数组或元祖中符合条件的元素。首先来看一个例子,选出1-10中的奇数:

    def is_Odd(list):
        newList = []
        for x in list:
            if x%2 :
                newList.append(x)
            else:
                pass
        return newList
    

    执行函数:

    list = [1,2,3,4,5,6,7,8,9]
    is_Odd(list)
    

    输出结果为:

    [1, 3, 5, 7, 9]
    

    再来看另一个例子,假设有一个标记文件路径的数组字符串数组exampleFiles赋值如下:

    exampleFiles = ["README.md", "HelloWorld.py","HelloSwift.swift", "HelloPython.py"]
    

    现在我们想从数组中取出.py数组,使用一个循环便可得到:

    def getPyFile(fileNames):
      newFileNames = []
      for fileName in fileNames:
          if ".py" in fileName:
             newFileNames.append(fileName)
      return newFileNames
    

    执行这个函数:

    fileNames =  ["README.md", "HelloWorld.py","HelloSwift.swift", "HelloPython.py"]
    getPyFile(fileNames)
    

    结果为:

    ['HelloWorld.py', 'HelloPython.py']
    

    从上述两个例子中可以得出两个方法的共同点,即返回那些函数(项)为真的序列项。如果函数是None,返回True的项,并返回一个列表。(如果序列是一个元组或者字符串,返回相同的类型)

    因此,有了上述map函数的基础,我们可以定义一个过滤器方法,将上述方法合二为一:

    def filter(function,sequence):
        list = []
        for x in sequence:
            if function(x):
                list.append(x)
        return list
    

    以筛选文件为例,执行这个方法:

    def getPyFile2(fileName):
      if ".py" in fileName:
           return fileName
       else:
           pass
    
    filter(getPyFile2,fileNames)
    

    执行这个函数,结果为:

    ['HelloWorld.py', 'HelloPython.py']
    

    与map相同,在python3中filter函数返回的是一个对象,需要加list转换成数组

    reduce()

    reduce意思是聚合,有了map和filter函数的研究基础,我们同样先来讨论一个简单的函数,定义一个函数计算数组中所有整数的和。

    python函数库中虽然自带有sum()函数,但我们仍自定义函数解决

    def sum(list):
        result = 0
        for x in list:
            result += x
        return result
    

    执行函数:

    list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    sum(list)
    

    结果为:

    45
    

    再来看另外一个例子:数以一组单词,将其拼成一个句子。例如将数组["I","am","a","good","boy"]拼接成一个字符串"I am a good boy"

    定义函数如下:

    def append(list):
        result = ""
        for x in list:
            result = result + x+" "
        return result
    

    执行函数:

    list = ["I","am","a","good","boy"]
    append(list)
    

    结果为:

    "I am a good boy "
    

    分析上面两个函数相同的地方,它们都用一些值初始化了一个变量result,它们进行处理的时候都遍历了整个数组list,并通过某种算法更新result。要定义实现这种通用算法的一个通用函数,有两处需要进行抽象:result的初始值以及在每个循环中用于更新result值的函数。

    因此我们可以定义一个函数函数满足上述需求,注意reduce函数最终返回的是一个value值而非数组:

    def reduce(function,sequence):
        result = None
        for x in sequence:
            result = function(result,x)
        return result
    

    其中function为需要操作的函数,sequence为数组,将resultsequence中元素操作后值赋予result,进行循环操作。

    执行上述函数操作便得以简化:

    def sum(a,b):
        return  a + b
    
    def append(a,b):
        return  a + " " + b
    
    reduce(sum,list)
    reduce(append,list)
    

    这我们得出所需要的reduce函数。在系统中的reduce定义为 reduce(function,sequence,initial=None),由于初值并非只有0或空字符串,可以为任意,因此需要赋初值,在这里给予一个初值变量并默认初值。将上述reduce函数改编为:

    def reduce(function,sequence,initial=None):
        result = initial
        for x in sequence:
            result = function(result,x)
        return result
    

    补充

    1.匿名函数

    在Python中,对匿名函数提供了有限支持。还是以map()函数为例,计算f(x)=x2时,需定义一个f(x)的函数,然后使用map函数,如:

    def f(x):
      return x * x
    
    list(map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    

    输出结果为:

    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    使用匿名函数,便能用一行解决:

    list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
    

    输出结果同样为:

    [1, 4, 9, 16, 25, 36, 49, 64, 81]
    

    从上述例子可以看到,匿名函数关键字为lambda,冒号前面的x表示函数参数。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

    用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数,例如:

    f = lambda x: x * x
    f(5)
    

    结果为:

    25
    

    因此,上述几段程序使用匿名函数便能简化很多:

    filter(lambda fileName:".py" in fileName,fileNames)
    reduce(lambda a, b: a + b,list)
    reduce(lambda a, b: a + " " +b,list)
    

    2.sorted函数

    将函数作为参数可不仅仅上述几个函数,比如sorted函数,顾名思义为排序,使用起来也很简单。

    sorted([36, 5, -12, 9, -21])
    [-21, -12, 5, 9, 36]
    

    在系统中,sort函数的定义为:

    def sorted(iterable, cmp=None, key=None, reverse=False)
    

    其高阶在于后面的三个参数。reverse参数是一个bool变量,是否倒序,默认值为False(正序)。key是关键字,是用于排序的对象。在上个例子中为数组本身,当然也可以对数组的个位数进行排序,如:

    sorted([36, 5, 12, 9, 21],key=lambda x:x%10)
    [21, 12, 5, 36, 9]
    

    cmp参数是比较的方法,加入我们要实现倒序而不是用reverse,可以这样写:

    sorted([36, 5, -12, 9, -21],cmp=lambda x,y:cmp(x,y))
    [-21, -12, 5, 9, 36]
    

    练习

    作为本文的结束,给出一个小例子供大家理解三个函数。(题目来源于 objc.io出版的《函数式编程》)

    有一组城市和人口数据如下:

    name: "Paris", population: 2243
    name: "Madrid", population: 3216
    name: "Amsterdam", population: 811
    name: "Berlin", population: 3397
    

    假如我们想要找出至少有1百万人口的城市并打印出它们的城市名和人口,输出结果为:

    City: Population 
    Paris : 2243000 
    Madrid : 3216000 
    Berlin : 3397000
    

    首先我们过滤掉小于100万人口的城市。然后使用map函数进行影射,将城市人口的单位进行转换。最后,我们使用reduce通过城市名和人口的列表计算得出一个字符串。这里我们使用了标准库的map,filterreduce函数。结果,我们可以像链条一样将这些函数串起来。

    作者:刘奇
    链接:http://keyliu.com/2018/04/19/python_func_1/
    转载请注明出处。

    相关文章

      网友评论

        本文标题:python学习笔记|函数式编程——map,filter,red

        本文链接:https://www.haomeiwen.com/subject/mdqbkftx.html