百日筑基篇—— 函数式编程(python入门四)
前言
在python中进行编程时,一般除了前面的过程式(使用语句、变量、函数)外,还有两种流行的编程模式:函数实编程、面向对象编程。在下文中,将详细介绍一下函数式编程风格。
一、函数式编程
函数式编程的核心思想是将问题拆分成一系列的函数,通过组合和变换这些函数来解决问题。
关键部分是高阶函数,将其它函数作为参数,或将其作为结果返回。
1. 纯函数
纯函数指的是没有副作用,并且对于相同的输入,总是得到相同的输出。
纯函数不会改变传入的参数,也不依赖于外部状态,这使得它们更加可靠和易于测试。
#纯函数
def chun_function(x,y):
example = x - y
return example / 2*y - x
#非纯函数
somelist = []
def nochun(x):
return somelist.append(x)
#因为该函数改变了somelist的状态
2. Lambda表达式
使用lambda语法创建的函数被称为匿名函数。
将一个简单函数作为参数传递给另一个函数时,常用这一方法。
#语法
lambda 参数列表: 表达式
#示例
def func(f, arg):
return f(arg)
print(func(lambda x: x ** 2, 5))
bianliang = (lambda x: x ** 2 + x / 2 + 2)(4)
print(bianliang)
3. map和filter函数
map()函数接受一个函数和一个可迭代对象作为参数,将该函数应用于可迭代对象的每个元素,并返回一个新的迭代器。
其实,与pandas中apply方法比较类似,只不过map函数更通用,适用于任何可迭代对象。
filter()它用于筛选出满足特定条件的元素,返回一个由满足条件的元素组成的迭代器或列表。
#map()
def add_six(x):
return x + 6
nums = [2, 4, 7, 5, 8, 3, 9]
print(list(map(add_six, nums)))
#或者
print(list(map(lambda x: x + 6, nums)))
#filter()
result = list(filter(lambda x: x % 2 == 0, nums))
print(result)
#结果
[2, 4, 8]
4. 生成器与装饰器
生成器是一种可迭代的类型,如列表或元组。
与列表不同的是,它们不允许使用任意索引,但是它们仍然可以通过 for 循环迭代。
可以使用函数和yield 语句来创建它们。
由于它们一次产生一个项目,所以生成器不具有列表的内存限制。
装饰器是修改其他函数的功能的函数,它基于函数或类的高阶特性,可以在不修改原函数或类的情况下,增加额外的功能或行为。
一个函数可以有多个装饰器。
#生成器
def count():
i = 6
while i > 0:
yield i
i -= 1
for i in count():
print(i)
#有限的生成器可以通过将它们作为参数传递给 list 函数来转换成列表
def number(x):
for i in range(x):
if i % 2 == 0:
yield i
print(list(number(10)))
#结果
[0, 2, 4, 6, 8]
#装饰器
def decor(func):
def wht():
print("$$$$$$$$$$$$$$$$")
func()
print("################")
return wht
def func():
print("my name is ***")
result = decor(func)
result()
#或者使用@
@decor
def func():
print("my name is ***")
func()
#结果
$$$$$$$$$$$$$$$$
my name is ***
################
补充说明:
普通函数的执行是一次性的,return语句会将函数的执行状态销毁。
使用生成器可以提高性能,这是懒惰(按需)生成值的结果,这意味着更低的内存使用率。而且,在开始使用之前,我们不需要等到所有的元素都被生成。
5. 递归
递归是函数式编程中一个非常重要的概念。
递归的基本部分是自引用 (调用自己的函数)。它被用来解决可以被分解成相同类型的更容易的子问题的问题。
#阶乘函数的递归实现
def func(x):
if x == 1: #设置基准情形,防止因无法退出而崩溃
return 1
else:
return x * func(x - 1)
print(func(6))
#间接的递归
def func1(x):
if x == 0:
return True
else:
return func2(x - 1)
def func2(x):
return not func1(x)
print(func1(15))
print(func2(15))
#结果
False
True
还有一个经典的递归实例:汉诺塔问题
它包括三根柱子和N个不同大小的圆盘。初始时,所有的圆盘都以从小到大的顺序堆叠在第一根柱子上,目标是将所有的圆盘移动到第三根柱子上,保持相同的堆叠顺序。
注意:
每次移动只能移动一个圆盘
小圆盘上不能放大圆盘
解题思路:
当N=2时,
- 把小圆盘从A移到B
- 把大圆盘从A移到C
- 把小圆盘从B移到C
其实,就是将B柱当作一个中转站
当N=N时
可以运用整体法(不是真的一次移动上面的N-1个圆盘,而是一步一步倒推回去,毕竟要想把最底层圆盘移动,必须要先移动上面的N-1个)
- 把N-1 个圆盘从A经过C再移动到B (将C当作辅助柱子)
- 把第N个圆盘从A移动到C
- 把N-1个小圆盘从B经过A移动到C (将A当作辅助柱子)
使用递归来移动前N-1个圆盘,并在移动第N个圆盘之前将其放在辅助柱子C上。然后,我们递归地将n-1个圆盘从辅助柱子A移动到目标柱子C。 每次递归都会将问题规模减小,直到最小规模的问题(只有一个圆盘)得到解决.
def hanoi(N, a, b, c):
if N > 0:
hanoi(N - 1, a, c, b)
print(f"from {a} to {c}")
hanoi(N - 1, b, a, c)
hanoi(3, "A", "B", "C")
#结果
from A to C
from A to B
from C to B
from A to C
from B to A
from B to C
from A to C
hanoi(2, "A", "B", "C")
#结果
from A to B
from A to C
from B to C
移动的总步数为2的N次方减1
6. itertools模块
itertools 模块是一个标准库,包含了几个在函数式编程中很有用的函数。
count() 从一个值无限增加
cycle() 无限次迭代(例如列表或字符串)
repeat() 重复一个对象,无论是无限还是特定的次数
chain () 将几个迭代结合成一个长整数
accumulate() 用于对可迭代对象中的元素进行累加操作
takewhile() 只会保留满足条件的连续元素,并在遇到首个不满足条件的元素后停止迭代,与filter()不同
product() 返回一个生成器迭代器,其中包含多个可迭代对象的所有可能的排列组合。
combinations(iterable, r): 返回可迭代对象中长度为r的所有组合
permutations() 返回可迭代对象中长度为r的所有排列,所有排列关注元素的顺序,而所有组合则不关注元素的顺序
from itertools import *
#count()
for i in count(2, 3):
if i > 20:
break
print(i)
#cycle()
name = ["mon", "son", "dad"]
for i in cycle(name):
print(i)
if i == "dad":
break
#repeat()
for i in repeat("enen", 3):
print(i)
#chain()
numbers = [1, 2, 3]
letters = ['a', 'b', 'c']
combined = chain(numbers, letters)
for i in combined:
print(i)
#combinations()
numbers = [1, 2, 3]
combs = combinations(numbers, 2) # 长度为2的所有组合
for i in combs:
print(i)
#输出
(1, 2)
(1, 3)
(2, 3)
#accumulate()
nums = list(accumulate(range(8)))
print(nums)
#输出
[0, 1, 3, 6, 10, 15, 21, 28]
#takewhile()
#只会保留满足条件的连续元素,并在遇到首个不满足条件的元素后停止迭代,与filter()不同
print(list(takewhile(lambda x: x % 2 == 0, nums)))
#输出
[0]
#组合函数
letters = ("星", "石")
print(list(product(letters, range(2))))
print(list(permutations(letters,2)))
#输出:
[('星', 0), ('星', 1), ('石', 0), ('石', 1)]
[('星', '石'), ('石', '星')]
总结
本章介绍了一下在python编程中常见的函数式编程风格,该模式最主要的还是应用各种高阶函数以达到将问题不断细化为一个个函数以及组合函数来实现问题的解决。好了,总结就到这里了。
寄蜉蝣于天地,渺沧海之一粟。
–2023-7-26 筑基篇