python学习笔记(四)——高阶函数

一、函数式编程

    函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。而函数式编程(-- Functional Programming),虽然也可以归纳到面向过程的程序设计,但其思想更接近”数学计算“。

    首先让我们明了计算机(Computer)与计算(Compute)的概念。在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。而计算则是指数学意义上的计算,越是抽象的计算,离计算机硬件越远。对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

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

    Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

二、高阶函数

    高阶函数英文叫Higher-order function。什么是高阶函数?我们以实际代码为例子,一步一步深入概念

    首先,变量也可以指向函数!

        以Python内置的求绝对值的函数abs()为例,调用该函数用以下代码:

abs(-10)

        但是,如果只写abs呢?

>>> abs

<built-in function abs>

        可见,abs(-10)是函数调用,而abs是函数本身。

        要获得函数调用结果,我们可以把结果赋值给变量:

>>> x = abs(-10)

>>> x

10

        但是,如果把函数本身赋值给变量呢?

>>> f = abs

>>> f

<built-in function abs>

    结论:函数本身也可以赋值给变量,即:变量可以指向函数。

    如果一个变量指向了一个函数,那么,可否通过该变量来调用这个函数?用代码验证一下:

>>> f = abs

>>> f(-10)

10

    成功!说明变量f现在已经指向了abs函数本身。直接调用abs()函数和调用变量f()完全相同。

    所以,函数名其实也就是变量!!!

    那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!如果把abs指向其他对象,会有什么情况发生?

    把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。要恢复abs函数,请重启Python交互环境。

    注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10

    既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。

def add(x, y, f):
    return f(x) + f(y)

res = add(-1, -19, abs)
print(res)

    这是一个最简单的高阶函数

    所以编写高阶函数,就是让函数的参数能够接收别的函数。

    小结:

        把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

二、map与reduce

    Python内建了map()和reduce()函数。

    (一)

        我们先看map。map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。举例说明,比如我们有一个函数f(x)=x2,要把这个函数作用在一个list [1, 2, 3, 4, 5, 6, 7, 8, 9]上,就可以用map()。

def f(x):
    return x * x
r = map(f,[1,3,5,7,9])
print(list(r))

        map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

        而map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x2,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:

    (二)

        再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

        让我们来看几个例子:

from functools import reduce

#累加得到1~9的奇数之和
def add(x,y):
    return x + y
num = reduce(add,[1,3,5,7,9])
print(num)

#计算5!
def prod(x,y):
    return x * y

res = reduce(prod, [1,2,3,4,5])
print(res)

#将列表[0~9]转换为int:1234567890
def fn(x,y):
    return 10*x + y
real = reduce(fn, [1,2,3,4,5,6,7,8,9,0])
print(real)
#这个例子本身没多大用处,但是,如果考虑到字符串str也是一个序列,对上面的例子稍加改动,配合map(),我们就可以写出把str转换为int的函数

#将字符串转换为int放入列表中
def char2num(x):
    #通过字典中键值对的方式进行数据类型更改
    digits = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9}
    return digits[x]

it = reduce(fn, map(char2num, ['1','2','3','4','5','6','7','8','9','0']))
print(it)

三、实验:

    str 转 float,即将一个例如‘1234.56789’的字符串转换为float。

    自己写函数,不使用python自带函数或方法。

#str 转 float
from functools import reduce
def str2float(x,y):
    #根据分析,点号前面的数值是x*10+y进行累加的,点号后则是x+y*10^n(n是y的位数)累加的,而当搜索到点号时,则返回x本身的规律,所以有了以下判断方式
    if L.index(y) < L.index('.'):
        return x*10 + y
    if L.index(y) == L.index('.'):
        return x
    if L.index(y) > L.index('.'):
        return x + y*(0.1**(L.index(y)-L.index('.')))

def char2num(x):
    #因为是浮点数,所以多了点号在字典中
    digits = {'0':0,'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'.':'.'}
    return digits[x]

L = list(map(char2num, list('3.456')))
print(L)
result = reduce(str2float, L)
print(result)
#问题,为什么当输入3.456时,输出是3.4559999999999995???

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值