python/函数式编程-高阶函数

文章目录

    • 函数式编程理解
    • 高阶函数(Higher-order function)
      • 函数名也是变量
      • 传入函数
      • map/reduce
        • map()
        • reduce()
        • 练习:
          • 1.利用map()函数,将英文名字变为首字母大写,其他小写的规范名字
          • 2.编写一个prod()函数,可以接受一个list并利用reduce()求积:
          • 3.利用map和reduce编写一个str2float函数,把字符串'123.456'转换成浮点数123.456:
      • filter(用于过滤序列)
        • 练习:回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
      • sorted
        • 练习:

函数式编程理解

对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

其可以定义为:

var result = subtract(multiply(add(1,2), 3), 4);

可以变换为:

add(1,2).multiply(3).subtract(4)

其只用"表达式",不用"语句":

高阶函数(Higher-order function)

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

a = abs
print(a(-10))--->10

函数名也是变量

函数名其实就是指向函数的变量!对于abs()这个函数,也可以吧abs看成变量,不过是指向了可以计算出绝对值的函数而已。
若令abs=10,abs(10)就会出错(实际写代码。不可以这么写)

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

传入函数

当一个函数接收另一个函数作为参数,这个函数就是高阶函数
而函数式编程就是指这种高度抽象的编程范式。
如:

def add(a,b,f):
    return f(a)+f(b)
 print(add(-1,1,abs))-->2

map/reduce

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

map()

map()函数接收两个参数,一个是函数,一个是Iterable(可迭代对象,字符串,列表,元组),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator(Iterator是惰性序列你不主动去遍历它,就不会计算其中元素的值)返回。

>>> def f(x):
...     return x * x
...
>>> r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> list(r)
[1, 4, 9, 16, 25, 36, 49, 64, 81]

注意:map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。
当然用for循环也可以实现;

L = []
for n in [1, 2, 3, 4, 5, 6, 7, 8, 9]:
    L.append(f(n))
print(L)
很难看出:把f(x)作用在list的每一个元素并把结果生成一个新的list

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

>>> list(map(str, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
['1', '2', '3', '4', '5', '6', '7', '8', '9']

reduce()

reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,
reduce产生的结果可以直接打印
reduce把结果继续和序列的下一个元素做累积计算,效果就是:

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

举例:

from funtools import reduce
def add(x,y):
     return x + y
reduce(add,[1,3,5,7])---->25

字符串转换

>>> from functools import reduce
>>> def fn(x, y):
...     return x * 10 + y
...
>>> def char2num(s):
...     digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
...     return digits[s]
...
>>> reduce(fn, map(char2num, '13579'))
>'不能直接输出map(char2num, '13579'),因为map()函数转换后的序列是惰性的,
>print(map(char2num, '13579')),打印的是map作用后序列的地址。'
13579

可整理为:

from functools import reduce
DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))

用lambda函数进一步简化成:

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def char2num(s):
    return DIGITS[s]
def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

练习:

1.利用map()函数,将英文名字变为首字母大写,其他小写的规范名字
L1 = ['adam', 'LISA', 'barT']
def normalize(name):
    name = name[0].upper()+name[1:].lower()
    return name
#map 每次是传入一个字符串'adam'
L2 = list(map(normalize, L1))
print(L2)
2.编写一个prod()函数,可以接受一个list并利用reduce()求积:
from functools import reduce
def prod(L):
    def mu(x,y):
        return x * y
    return reduce(mu,L)
print(prod([3,5,7,9]))
3.利用map和reduce编写一个str2float函数,把字符串’123.456’转换成浮点数123.456:
from functools import reduce
DIC = {'.':'.','0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
def str2float(s):
    def str2num(s):#字符转换为数字
        return DIC[s]
    def cal(x, y): #计算
        return 10*x +y
    L = list(map(str2num,s))#将字符串中的所有字符映射为对应的数字和标点,
    n = L.index('.')#获取位置
    n1 = L[0:n]
    n2 = L[n+1:]
    return reduce(cal,n1) + reduce(cal,n2)/10**len(n2)
    #print(L)
print(str2float('123.369'))

filter(用于过滤序列)

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

def is_odd(n):
    return n % 2 == 1
list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。
例子:
计算素数

def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n
def _not_divisible(n):
    return lambda x: x % n > 0
def primes():
    yield 2
    it = _odd_iter() # 初始序列
    while True:
        n = next(it) # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it) # 构造新序列
for n in primes():
    if n < 50:
        print(n)
    else:
        break

练习:回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:

def is_palindrome(n):
    return str(n)==str(n)[::-1]
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
    print('测试成功!')
else:
    print('测试失败!')

sorted

在排序中:
如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。
sorted()函数就可以对list进行排序:

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

sorted()也是一个高阶函数,可以接收一个key函数来实现自定义的排序
例如按绝对值大小排序:

>>> sorted([36, 5, -12, 9, -21], key=abs)
[5, 9, -12, -21, 36]

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序

也可以对字符串进行排序:
(比较是按照ASCII码值进行比较的)

>>> sorted(['bob', 'about', 'Zoo', 'Credit'])
['Credit', 'Zoo', 'about', 'bob']

也可以对字符串的的小写进行排序:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']

反向排序:
不必改动key函数,可以传入第三个参数reverse=True:

>>> sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower, reverse=True)
['Zoo', 'Credit', 'bob', 'about']

练习:

L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
#sorted每次作用一个元素,如第一次取('Bob', 75)
名字排序:
def by_name(t):
    return t[0]
L2 = sorted(L, key=by_name)
print(L2)
成绩从高到低排序:
def by_score(t):
    return t[1]
L2 = sorted(L, key=by_score)
print(L2)

你可能感兴趣的:(笔记)