Python程序设计 实验6:函数式编程
Posted 上山打老虎D
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python程序设计 实验6:函数式编程相关的知识,希望对你有一定的参考价值。
1. 函数的参数传递
定义一个简单的函数 sum 如下
def sum(a, b, c):
print("a=%d, b=%d, c=%d"%(a,b,c))
print(a+b+c)
以下哪些语句是合法的,哪些是不合法的?分别输出什么?解释原因。
a) sum(*(1, 2, 3))
答:
语句合法。
输出如下:
三个参数均通过*param形式实现了可变数量参数,并作为a,b,c三个参数调用函数进行运算。
b) sum(1, *(2, 3))
答:
语句合法。
输出如下:
第一个参数a直接传参,后两个参数通过*param形式实现了可变数量参数,并作为b,c两个参数调用函数进行运算。
c) sum(*(1,),b=2, 3)
答:语句非法。并报错:
SyntaxError: positional argument follows keyword argument。即关键字参数必须跟随在位置参数后面。由于如果使用关键字赋值就需要都使用,这里只使用了一个关键字参数赋值,因此报错。
d) sum(*(1,),b=2, c=3)
语句合法。
输出如下:
具体同上。如果使用了关键字参数赋值,则都需进行赋值。代码中都对关键字进行了赋值。因此语句合法。
e) sum(*(1, 2),c=3)
语句合法。
输出如下:
同c题,本题中只是多了一个可变数量参数,少了一个关键字赋值。仍然满足“如果使用了关键字参数赋值,则都需进行赋值”的原则,故语句合法。
f) sum(a=1, *(2, 3))
语句非法。并报错:
TypeError: sum() got multiple values for argument ‘a’。即参数‘a’被多次赋值。这是由于首先通过关键字赋值对a进行了赋值后,又通过可变数量参数进行了赋值从而导致错误发生。
g) sum(b=1, *(2, 3))
语句非法。并报错:
TypeError: sum() got multiple values for argument ‘b’。原因同上。即参数‘b’被多次赋值。这是由于首先通过关键字赋值对b进行了赋值后,又通过可变数量参数进行了赋值从而导致错误发生。
h) sum(c=1, *(2, 3))
语句合法。
输出如下:
与f题,g题原因相同。首先利用关键字赋值对变量c进行了赋值。其次通过可变数量参数对‘a’和‘b’进行了赋值,三个变量都被赋值且未被重复赋值,故语句合法。
2. Lambda
思考以下句子的输出,解释每句话意思,并通过实际测试验证自己想法。
2.1 (lambda val: val ** 2)(5)
①输出结果: 25
②代码分析: 求输入值的平方。根据Lambda表达式,可以看出,该表达式的值为输入数据的2次方
③进行测试:
a)输入-4:输出16
b)输入0:输出0
c)输入9:输出81
结合数据测试以及理论分析,可以得出,该Lambda表达式的意思为求一个数的平方。
2.2 (lambda x, y: x * y)(3, 8)
①输出结果: 24
②代码分析: 求输入两数之积。根据Lambda表达式,可以看出,该表达式的值为输入两个数字的乘积。
③进行测试:
a)输入-4,-6:输出24
b)输入9,0:输出0
c)输入9,5:输出45
结合数据测试以及理论分析,可以得出,该Lambda表达式的意思为求一个数的平方。
2.3 (lambda s: s.strip().lower()[:2])(’ PyTHon’)
①输出结果: py
②代码分析: 求输入字符串中前两个非空格非换行小写字母(如原来为大写,则转换为对应小写字母)
首先通过string.strip()函数从头部丢弃空格以及换行符。再利用string.lower()函数将对应大写字母转为小写后利用切片得到前两个字符
③进行测试:
a)输入 I love :输出i (i后有一空格)
b)输入 456:输出45
c)输入 @aa:输出@a
结合数据测试以及理论分析,可以得出,该Lambda表达式的值为第一个非非空格非换行(大写对应)小写字母,以及其后的一个(大写对应)小写字母或其他字符。
3. Map
使用 map 语句将以下输入,分别转化为指定的输出。
3.1 [‘12’, ‘-2’, ‘0’] --> [12, -2, 0]
①大致思路:
使用map将对应字符串型转换为整型即可。
②编写代码:
print(list(map(lambda x: int(x), ['12','-2','0'])))
此处借助了Lambda表达式,将字符型转成整形,并借助list完成输出。
③运行测试:
3.2 [‘hello’, ‘world’] --> [5, 5]
①大致思路:
使用map获得每个字符串的对应长度即可。
②编写代码:
print(list(map(lambda x: len(x), ['hello', 'world'])))
③运行测试:
3.3 [‘hello’, ‘world’]` --> [‘olleh’, ‘dlrow’]
①大致思路:
使用map将字符串翻转即可。
②编写代码:
print(list(map(lambda x: x[::-1], ['hello', 'world'])))
此处借助了Lambda表达式并利用字符串切片获取反向字符串,并借助list完成输出。
③运行测试:
3.4 range(2, 6) --> [(2, 4, 8), (3, 9, 27), (4, 16, 64), (5, 25, 125)]
①大致思路:
使用map依次求出给定数字的一次方,二次方,以及三次方。
②编写代码:
print(list(map(lambda x: (x,x**2,x**3), range(2,6))))
此处借助了Lambda表达式,利用range获得范围并获取一次方,二次方,三次方的数值,并借助list完成输出。
③运行测试:
3.5 zip(range(2, 5), range(3, 9, 2)) --> [6, 15, 28]
①大致思路:
通过zip收集后即为[(2, 3), (3, 5), (4, 7)],因此函数的大意为求两个数字的乘积。由于zip返回的是元组打包成的列表,因此要对元组进行操作,而不能对两个数字进行操作。
②编写代码:
print(list(map(lambda x: x[0]*x[1], zip(range(2, 5), range(3, 9, 2)))))
此处首先借助Lambda表达式,并利用zip收集两个range获得范围。由于zip返回的是由元组构成的列表,因此Lambda中的变量为元组,故返回元组第一个值与第二个值的乘积即可。
③运行测试:
4. Filter
使用 filter 语句将以下输入,分别转化为指定的输出。
4.1 [‘12’, ‘-2’, ‘0’] --> [‘12’, ‘0’]
①大致思路:
观察题目,可以知道,filter保留下转成整型后大于等于零的字符串。因此使用filter并借助Lambda表达式可以比较简单又有效的完成这个任务。
②编写代码:
print(list(filter(lambda x: int(x)>=0 , ['12','-2','0'])))
利用Lambda作为函数,对给定列表中每个元素进行转成0后是否大于等于0进行判断并过滤。
③运行测试:
4.2 [‘hello’, ‘world’] --> [‘world’]
①大致思路:
观察题目,可以知道,filter保留下转成首字母为w的字符串。因此使用filter并借助Lambda表达式可以比较简单又有效的完成这个任务。
②编写代码:
print(list(filter(lambda x: x[0]=='w' , ['hello', 'world'])))
③运行测试:
4.3 [‘technology’, ‘method’, ‘technique’] --> [‘technology’, ‘technique’]
①大致思路:
观察题目,可以知道,filter保留下转成首字母为t的字符串。因此使用filter并借助Lambda表达式可以比较简单又有效的完成这个任务。
②编写代码:
print(list(filter(lambda x: x[0]=='t' , ['technology', 'method', 'technique'])))
通过借助Lambda表达式对字符串第一个字符直接访问并判断是否为t从而进行过滤。
③运行测试:
4.4 range(20) --> [0, 3, 5, 6, 9, 10, 12, 15, 18]
①大致思路:
观察题目,可以知道,filter保留三或五的倍数。因此使用filter并借助Lambda表达式对数字进行取余判断余数是否为零以完成这个任务。
②编写代码:
print(list(filter(lambda x: x%3==0 or x%5==0 , range(20))))
③运行测试:
5. Reduce
使用 reduce 语句编写函数 lcm(*nums),计算任意数量个正整数的最小公倍数,要求只写一句 python 语句 (提示:可使用 math 模块的 gcd 函数先求出最大公约数)。
例子:
lcm(3, 5) # 15
lcm(41, 106, 12) # 26076
lcm(1, 2, 6, 24, 120, 720) # 720
lcm(3) # 3
lcm() # 如果没有向函数提供数字,可以返回值1。
①大致思路:
最小公倍数等于两数乘积除以最大公约数。因此可以借助Lambda表达式进行运算。此处需要注意,必须要给函数赋初始值1,否则当函数的输入参数个数为零时,将报错。
②编写代码:
def lcm(*nums):
return int(reduce(lambda x, y: x*y/math.gcd(int(x), int(y)), nums, 1))
使用Lambda表达式,将每两个元素的返回值设置为两数之积与两数最大公约数的商。在reduce内,以Lambda作为函数,以nums作为范围,并设置默认函数返回值1。需要注意的是,此处必须设置返回值为1,否则当nums中参数个数为0时,将报错。
③运行测试:
使用几个数字进行测试结果如下:
a)参数全部缺省:
b)参数缺省一个:
c)拥有两个参数
d)拥有很多参数
6. Iterator
运行以下代码,观察输出并解释输出的原因。
it = iter(range(100))
67 in it
print(next(it))
print(37 in it)
print(next(it))
①运行代码:
即print(next(it))输出了68,print(37 in it)输出了False。print(next(it))发生了StopIteration迭代结束异常。
②分析原因:
首先,定义了it为从0到100范围内的迭代器。第二行即查找67是否在迭代器中,此时迭代器位置在67处。然后第三行将迭代器后移一个并输出,因此此时应输出68。
第四行查查37是否在迭代器中,由于此时迭代器位置在68且迭代器不能向前移动,故迭代器将一直移动到末尾,并返回False。
由于第四行中对37的查找使迭代器移动到迭代对象末尾,因此再调用next函数对迭代器进行后移时,将报错StopIteration。
7. Generator
7.1 编写一个生成器 generate_triangles(),连续地产生三角数 1,3,6,10,… 三角数通过连续的正整数相加来生成(如 1=1,3=1+2,6=1+2+3,10=1+2+3+4,…)。
例子:
g=generate_triangles()
for _ in range(5):
print(next(g)) #输出 1,3,6,10,15
①大致思路:
由于三角数每一个数都等于前一个数加上第几个数。因此可以借助生成器实现快速生成。即在yield中定义,每一个数都等于前面的数字加上计数器再加一即可。
②编写代码:
# 定义生成器
def generate_triangles():
a, n = 1, 1
while True:
yield a
# 每一个元素都等于上一个元素加第几个元素再加一
a += n+1
n += 1
# 进行测试
it = generate_triangles()
for _ in range(5):
print(next(it))
首先定义生成器,由于每一个数都等于前面的数字加上计数器再加一。因此只需定义当前值与计数器,首先,将当前值加上计数器再加一。然后将计数器加一即可。
③运行测试:
7.2 使用生成器 generate_triangles(),编写函数generate_triangles_under (n),返回小于 n 的所有三角数。
①大致思路:
使用生成器进行迭代生成,并依次进行判断,如果小于n则加入到结果列表中,否则直接返回即可。
②编写代码:
# 定义生成器
def generate_triangles():
a, n = 1, 1
while True:
yield a
# 每一个元素都等于上一个元素加第几个元素再加一
a += n+1
n += 1
# 定义小于n的三角数函数
def generate_triangles_under(n):
res = []
it = generate_triangles()
temp = next(it)
# 如果大于则直接退出循环
while temp < n:
# 如果小于则加入到列表中
res.append(temp)
temp = next(it)
return res
print(generate_triangles_under(10))
调用生成器生成每一个值,并依次对每个值进行判断,如果生成的值小于n,则加入到结果列表中,否则直接返回。
③运行测试:
实验结论
本次实验主要是对函数式编程的理解以及使用。分别学习了函数的传参,Lambda表达式,Map,Filter,Reduce,迭代器和生成器的使用。通过这些连续,我初步掌握了这些函数式编程技巧的使用。
本次实验还算比较顺利,仅有一些地方遇到了困难,在查阅资料后也都得到解决。
- 在可变数量参数函数中,需要注意函数的初始值。在本实验中,lcm函数求最小公倍数时,就必须注意给函数赋初始值,否则如果参数个数为0,则将报错。
- Lambda可以减少代码量并提高可读性:在本实验进行过程中,很多代码都是使用Lambda表达式进行完成,最后编写的代码不仅比较短,而且可读性都比较高。因此,在进行函数式编程时,可以采用Lambda表达式来减少代码量并提高可读性。
- 定义生成器时,务必要搞清楚生成数值的过程。本实验中,生成器一部分是相对耗时比较长的,究其原因是不能正确理解生成器生成对应数据时的过程。因此,搞清楚生成数据的过程,才能进行实验。
以上是关于Python程序设计 实验6:函数式编程的主要内容,如果未能解决你的问题,请参考以下文章