【习题】
【1】汉诺塔的移动
【2】自定义strip方法
【4】打印杨辉三角
【6】打印所有素数
【7】将L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]排序
【8】设计一个decorator,使它能作用于任何函数,并打印出函数执行的时间
【9】将题【8】中的装饰器作用于一个打印素数的函数,要求输入素数的最大值
【10】将题目改造成class
【11】让用户输入名字,再次运行程序后仍可对用户表示欢迎(并确定用户是否为上次运行该程序的用户)
【课外练习】
【1】定义阶乘函数
【2】记由1,2,3,4组成的三位数为x,写出所有无重复数字的x
【3】一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
也就是,x为整数,n1,n2也为整数,,
【4】打印九九乘法表
【6】将一个正整数分解质因数
【7】输入一个成绩,符合{'A': >= 90 丨'B': 60 <= x < 90丨'C': < 60 },分别输出等级
【8】一个数如果恰好等于它的因子之和则称为"完数"。例如6=1+2+3.编程找出1000以内的所有完数
【9】打印一个菱形图案
【11】按逗号分隔列表,如L = [1,2,3,4,5],返回1,2,3,4,5
【零落的知识点】
① sum( list ) = reduce( lambda x,y : x+y , list )
② L[:1] = L[0]
③ num = 123;num = input( )、输入123;这两者获得的123类型都为str,而不是int
④ 习题中有一题是比较输入的两个值,Maximum = lambda x,y : (x>y)*x + (x<y)*y,式子中的(x>y)和(x<y)都是布尔表达式,当为True时显示为1、为False时显示0;将0和1代入式子中,恰好可以计算出最大值。(当然,存在x=y时的bug)
⑤ 自定义int( )函数:return reduce( fn , list(map(int , n)))
⑥ 高阶函数可被简化:
map(add_10 , [1,2,3]) --> [add_10(i) for i in [1,2,3]]
reduce(lambda x: x > 5 , [1,2,8,5,9]) --> [x for x in [1,2,8,5,9] if x > 5]
【答案】
【1】汉诺塔的移动
PS:从抽象层面上去理解
【2】自定义strip方法
PS:从这题中可看出,对递归和切片的运用还很不熟练
【3】打印前n个斐波那契数列的值
方法①:
PS:利用同行赋值式,你应该记得上面的a , a+b被组装成一个排序
方法②:
PS:结合斐波那契数列的特性,先设立特殊情况【即前两个[1]】,再利用Python从后遍历,使用append
方法③:(输入x,若仅显示第x个数,而不是显示从1到x的整段数列)
PS:理解斐波那契数列的特性,先设立特殊起始值,通过递归,在抽象层面上解决问题
方法④:(与方法③相似,都是运用递归,但可显示1到x的整段数列)
PS:输入x,方法③只能显示第x个数值,如此,利用map遍历range(1,x+1)得到整个Iterator。问题是它似乎占用很大内存,调用Fib【20】有时会长时间无法计算出结果笔记{尾递归}中有问题的补充,原因是其递归算法造成极大的冗余调用,复杂度是指数级的
方法⑤:(这是对方法④的改进,解决递归的低效率问题)
PS:两者的Fib函数没有不同,但这里的fib函数其实是模仿方法①的同行赋值式a,b = b,a+b,设立特殊值(n = 1和n = 2),设立算法的最终输出(n = 3时返回a+b,符合斐波那契数列的定义),通过递减n控制次数,在次数限制下不断对初始的a = 1 , b = 1进行逻辑运算,得到结果
由代码可看出,每到递归的步骤时仅对函数自身调用1次,由于n是递减的,所以这个函数的复杂度是线性的;由于Python默认限制最大递归深度为997,所以该代码可计算Fib(900)无压力
方法⑥:(非函数在模块中被导入,而是直接运行.py文件)
PS:
这里最重要的一点是最后的print( )函数,作为函数的返回值返回后,在命令行窗口会直接输出;但运行.py文件却不会,即使返回一个list,也没有标明对它的操作是什么,因此这时借助print( )函数输出
此外,对高阶函数的运用还不到位,map都丢到角落不会用了,傻傻地1行代码写成4行...
【4】打印杨辉三角
———————————————————————————————————————————————————
PS:思路有了,但对数值的把握火候不够,n的初始值总是要进过调试才能确定...不知道这是否正常
PS:渐渐开始熟悉了:
①假设列表L存在,由于规则为相邻两个数值相加,可通过range( len(L) -1 )来控制本次相加的次数
②通过max值来控制总的L的相加次数
【5】使用reduce和map重新定义函数float( )
———————————————————————————————————————————————————
涉及字典dict、lambda、index
【6】打印所有素数
① 定义全体自然数N,用n = N( )获取Iterator
② 定义筛选函数not_division,注意这里的双变量使用(注:这里的filter中的函数可以接收参数)
③ n1 = next(n)获取Iterator的第一个值,返回第一个值后,用filter进行筛选,n中但凡是可以整除n1的都会被删去,包括与n1等值的n的第一个值。
④ 不断重复步骤③,不断获取第一个值
【7】将L = [('Bob', 75), ('Adam', 92), ('Bart', 66), ('Lisa', 88)]排序
PS:由于各个元素为tuple,匿名函数旨在单独取出各个tuple中的字符串或数字
【8】设计一个decorator,使它能作用于任何函数,并打印出函数执行的时间
PS:注意执行函数func需在定义start和end之间;以及最后记得返回wrapper
记得语法糖@的含义,以及使用它时的位置
【9】将题【8】中的装饰器作用于一个打印素数的函数,要求输入素数的最大值
PS:①原primes为迭代器,要想被装饰,修改为函数(通过提前设立max、将yield改为print)
②严格遵照了装饰器(Docorator)的模板
③采用if __name__ == '__main__'的方式减少繁杂的程序测试
【10】将斐波那契数列题目/打印素数题目修改为class
PS:通过input、初始化self.max可以达到设置退出循环条件的目的~
PS:这里锦上添花,运用了\r和time模块的sleep函数,效果不错,但是被注释掉的那行如果替代现在的print,(也就是我想输出一长列的元素,但每个稍有些间隔才出现),会发现无法退出循环【??】
【11】让用户输入名字,再次运行程序后仍可对用户表示欢迎(并确定用户是否为上次运行该程序的用户)
PS:多练习,这是经过重构的代码
【课外练习答案】
【1】定义阶乘函数
方法①:(使用高级函数)
方法②:(利用递归)
【2】记由1,2,3,4组成的三位数为x,写出所有无重复数字的x
PS:思维局限在要打印一个完整的数字,殊不知可以将一个数字看成是多个数字字符串的并列组合
【3】一个整数,它加上100后是一个完全平方数,再加上168又是一个完全平方数,请问该数是多少?
即,,其中x为整数,n1,n2也为整数
PS:在草稿纸上列出数学关系式,即题目的第二行
①先找出n2的最大值:由于n1、n2同为完全平方数,所以假设它们之间的间隔为最小值1,又已知它们平方后的间隔为168,所以通过while不断接近n2的近似最大值;通过求出n2的近似最大值,可以减少计算机的无效计算
②通过列表生成式将分别满足条件n1、n2的x筛选出来,设为x1、x2,;由于n2为最大值,即n2 > n1,n1的范围是n2的子集,所以所求的n2近似最大值可以作用于n1
③求出x1、x2后,由于x1、x2分别为一半题目的解,这时题意就变为求同时满足x1、x2的x的所有可能;通过set( )将list转变为无序且不重复的集合,再将set进行数学的集合运算,即求交集,得到答案x
最后,上述列表生成式可以用高阶函数map和lambda取代:
【4】打印九九乘法表
PS:其中涉及print函数中的end用法,详见参考笔记O(∩_∩)O
【5】求101到200之间的所有素数及个数
首先这是一个错误的范例:
调试结果表明,如果连续出现3个合数,如104,105,106,上述方法会将中间的105忽略!
原因:Python中for循环是根据下标的,上述方法中,当remove(104)后,原本{104}所属的索引位4被空缺,接由后面的{105}取代索引位4;break后,for循环依据下标,直接对索引位为5的元素进行逻辑运算,就这样,索引位为4的{105}被错过;事实上,当{102}被remove掉,后面的{103}也没有被检测,尽管{103}是素数,但这种筛选方法显然是有漏洞的
正确范例:
修改后的代码对L使用了切片复制得到L_copy,在判断时使用的是L中的元素,但删除的却是L_copy中的元素,这样,for循环迭代L中元素时不会出现遗漏
方法②:(待删)
PS:我之前就疑惑如何完全删除一个数列中所有的某个元素,直到复习时无意发现结合使用while和in可以重复删去,这才成功;然后就是定点删除的问题,list没有remove这个属性,属于检测到x为合数时,没办法直接删去x,所以只能使用手动下标,将合数先转变为空格,再将list中的所有空格删去
【6】将一个正整数分解质因数
PS:该题较为简单,复习时可尝试使用range;有人先用生成器生成一个素数的无限数列,然后将参数n逐个除以素数,并输出该素数和修改参数n
【7】输入一个成绩,符合{'A': >= 90 丨'B': 60 <= x < 90丨'C': < 60 },分别输出等级
PS:这是一种暂时还没学的方法,该方法在Python 3.x上运行是ok的,从代码的内容和运行情况可以大致了解一下这种方法。
①格式为print([...][...])
②前一个[...]中输入若干个字符串,字符串按顺序排列,拥有索引0、1、2等
③后一个[...]对输入的参数进行判别且必须进行参数运算,单纯地[score]会报ValueError,所以哪怕是无意义的[score//1]也必须敲上;对参数进行运算后(即上述的score//30),根据得到的结果分别对应索引号输出相应的字符串,若无对应的索引号,将会报IndexError
以上面代码为例,前一个[...]共有4个字符串等待输出,则拥有4个索引号,即0、1、2、3;又因为后面的运算为[score//30],要想参数score地板除30得到相应的索引号,则score的范围为[0,120];又由于Python支持倒数遍历,所以索引号-1、-2、-3、-4都是允许的,所以参数score范围实为[-120,120],超出范围即报IndexError。
在符合范围的前提下,每运算出一个一个相应的索引号,就会print相应的字符串
【8】一个数如果恰好等于它的因子之和则称为"完数"。例如6=1+2+3.编程找出1000以内的所有完数。
PS:不难,待删
【9】打印一个菱形图案
效果图:
PS1:我原本的思路是根据max得到L = ' '*max,算出max中间那个数middle,定义逐渐递增的num,使范围在(middle - num , middle + num)中的所有空格替换成星号'*';问题是字符串在Python中是不可修改的,修改的方法
①replace,这个方法中如替代L[0]为L[0].upper( ),所有为L[0]的字符都会被替换,而不只是L[0]处的字符
②join,似乎可行,但还没学 【所以以后学了试试?】
③切片,字符串也是序列,可以使用序列下标的方式,即L[:3]+'*'*num+L[-3:]
惊觉方法③有可能成功,然后马上试验:
PS2:这里有几点说明下:
①改善了原问题,使规定打印星号{*}的菱形变为打印输入的某个字符,即用string替代 '*'
②num -= 4的原因在图中有说明
③实际上,L[:l_num] + string*num + L[r_num:]中的数据没有经过准确推敲,而是凭感觉敲上去的
④若输入的string为中文汉字,由于汉字所占的字节数为2,而空格为1,所以不能形成规则菱形
⑤这里的类型检查似乎不行,if not ( isinstance ( string , str ) ) or ( isinstance ( max , int )) 已解决,应为if not ( isinstance ( string , str ) ) and ( isinstance ( max , int )),and和or使用错误
PS3:我是认定每一行长度皆为max,在这个基础上敲代码;另一种思路是只看每行星号前的空格数、星号数,星号后面的就由它去吧。上面的代码就是一个例子
【标准答案涉及stdout】
【10】利用递归,将输入的一个字符串倒序输出
PS:递归时注意len(n)这个数就比n最后一个字符的索引大1,所以即使切片[x:y]不取y,也要减去1
【打印出的每个字符都间隔一个空格,尝试将空格删除?】
【11】按逗号分隔列表,如L = [1,2,3,4,5],返回1,2,3,4,5
PS:这题旨在剥除list的外壳,将里面的元素逐个返回,关键点在于打印逗号,可用[1:-1]去除首端的空格和末尾的逗号
PS:同样可通过print来实现,注意if和else的位置,且之间无需其他分隔符
【此外,有的答案还涉及join、repr】