函数式编程
高阶函数
一个函数可以接收另一个函数作为参数,这种函数就称之为高阶函数。
map/reduce
map()
map()函数接收两个参数,一个是函数,一个是Iterable(可迭代对象),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator(迭代器)返回。
例如:使用map函数生成一个x²的数列
def f(x):
return x * x
r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
print(list(r))
[1, 4, 9, 16, 25, 36, 49, 64, 81]
map函数返回的是一个迭代器,使用list()函数将map结果变成列表输出。
list()函数的参数必须是一个可迭代对象,用于生成列表。
将L1中的名字规范为首字母大写,其他字母小写的形式:
def normalize(name):
str_format = name[0].upper() + name[1:].lower()
return str_format
# 测试:
L1 = ['adam', 'LISA', 'barT']
L2 = list(map(normalize, L1))
print(L2)
['Adam', 'Lisa', 'Bart']
reduce()
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
例如对一个序列求和:
from functools import reduce
def add(x, y):
return x + y
a = reduce(add, [1, 3, 5, 7, 9])
print(a)
25
map与reduce结合使用
例如:把字符串’123.456’转换成浮点数123.456:
def str2float(s):
num = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
a = reduce(lambda x, y: x * 10 + y, map(lambda x: num[x], s.split('.')[0]))
b = reduce(lambda x, y: x * 10 + y, map(lambda x: num[x], s.split('.')[1]))
c = b/10**len(s.split('.')[1])
return a + c
print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.456') - 123.456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')
输出结果:
str2float('123.456') = 123.456
测试成功!
filter
filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数:
def is_odd(n):
return n % 2 == 1
L = list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
print(L)
# 结果: [1, 5, 9, 15]
用filter求素数:
计算逻辑:从2开始的自然数序列,2为素数,先筛选掉2的倍数,剩下的序列中第一个数为3是素数,再筛选掉3的倍数,以此类推
# 构建一个从3开始的奇数数列
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
# 建立筛选规则
def _not_divisible(n):
return lambda x: x % n > 0
# 利用filter创建筛选之后的序列,构建生成器
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
# 打印100以内的素数:
for n in primes():
if n < 100:
print(n)
else:
break
用filter筛选回数
def is_palindrome(n):
s = str(n)
return s == s[::-1] # s[::-1]表示从字符串 s 的末尾到开头,以步长为 -1 进行切片,即反转字符串。
# 测试:
output = filter(is_palindrome, range(1, 100))
print('1~100:', list(output))
1~100: [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99]
sorted
Python内置的sorted()函数就可以对list进行排序:
>>> sorted([36, 5, -12, 9, -21])
[-21, -12, 5, 9, 36]
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序
L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]
def by_name(t):
name = t[0].upper()
return name
def by_score(t):
score = t[1]
return score
L2 = sorted(L, key=by_name)
print("按照名字排序:", L2)
L3 = sorted(L, key=by_score, reverse=True)
print("按照成绩从高到低排序:", L3)
按照名字排序: [('Adam', 92), ('bart', 66), ('Bob', 75), ('Lisa', 88)]
按照成绩从高到低排序: [('Adam', 92), ('Lisa', 88), ('Bob', 75), ('bart', 66)]
reverse=True参数表示进行方向排序
返回函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
例如,定义一个求和函数的惰性计算函数:
def lazy_sum(*args): # *args 是一个特殊的语法,用于在函数定义中表示可以接受任意数量的位置参数。这些参数会被收集到一个元组中。
print('-> args:', args)
def sum():
ax = 0
for n in args:
ax = ax + n
return ax
return sum
f = lazy_sum(1, 3, 5, 7, 9)
print(f())
-> args: (1, 3, 5, 7, 9)
25
⚠️函数sum()是lazy_sum的返回函数,f = lazy_sum其实是将f指向了sum(),只有调用f()才会执行求和计算得到计算结果。
⚠️内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种是称为“闭包(Closure)”的程序结构。
⚠️调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数。
使用闭包时,对外层变量赋值前,需要先使用nonlocal声明该变量不是当前函数的局部变量:
def createCounter():
x = 0
def counter():
nonlocal x
x = x+1
return x
return counter
# 测试:
counterA = createCounter()
print(counterA(), counterA(), counterA(), counterA(), counterA())
1 2 3 4 5
匿名函数
关键字lambda表示匿名函数,冒号前面的x表示函数参数,冒号后面为函数体。
例如:
list(map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9]))
lambda x: x * x就相当于:
def f(x):
return x * x
匿名函数只能写一个表达式,返回值为表达式计算结果,不需要写return。
匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数:
f = lambda x: x * x
print(f(2))
4
也可以把匿名函数作为返回值返回:
def build(x, y):
return lambda: x * x + y * y