一道简单的程序题?-Python高手成长路

本篇摘要

在学习程序语言基础知识的时候,大概会找书上、教学视频上的一些练习题来练手。有些题它看似简单,实际上实现起来确实是挺简单的,初学者一般确定好一个思路,做完题目运行一下,看到结果正确,就接着练下一题了。但有的练习题虽然简单,却可以因为有更好的思路,做出不一样的解答,达到易读、运行效率高的效果。

在编程的领域里,没有标准答案,只有更好的答案。通过一道题举一反三,学习更多的知识,积累更丰富的经验。

文末会附上新的作业题,欢迎在留言区踊跃参与。

往期精选

>>>

Python从哪里开始学好呢?

先来看看这道简单的程序题是怎样的。

· (题目) 从1到9共9个数字,计算并输出所有可能组成的n位数(3

输入:n

输出:所有符合条件的n位数(如n=3时,123、124、986、987都符合条件,而988、999不符合条件)

如果这道题要求的是“从1到4共4个数字,组成每位不重复的3位数”,初学者第一思路可能是这样子的,写四个嵌套的for循环,先把这种3位数拼出来,然后输出。正常情况下调试好输出的结果并没有什么意外,毕竟4个数字拼成3位数,效率再低的代码风格在普通的PC上怎样都是秒出结果的。

但看到“从1到9共9个数字”“组成的n位数”的时候,可能就一脸懵了,怀疑人生式地问自己:这难道是要叫我写n个for循环嵌套来计算输出结果吗?

在这道题上,嵌套for循环的方式并不是最佳的,写起来啰嗦,难维护,而且很难控制它输入n之后就做n个嵌套for循环。

这可如何是好?

这里有两个思路提供参考,一个是推导式与生成器,一个是“递归”算法。

01

思路1:推导式与生成器

先来看看酷友 @skywet 的这段代码答案,如图。

他的思路是这样的:

① 让用户循环输入n,直到n符合要求时开始计算;

② 构造一个生成器对象g,该生成器用来产生n位数的所有可能的数字(并未进行实际生成数字);

③ 循环检查生成器生成的每一个数,检查时先转换成字符串,然后判断这个数是否没有出现数字“0”,且通过转换成set(集合)类型判断是否没有出现重复数字,这两点都符合要求的,放到列表里面;

④ 输出列表里面所有数字,即题目答案;

QA问答环节

Q1:“生成器”是啥子?

A:简单地说,在Python里面为了节约系统资源、提高运行效率,有一种可以边循环边计算的机制,称为“生成器”。也就是说,思路第②原本是可以通过循环产生所有可能的n位数的,用“生成器”优化后,程序运行效率更高。使用方法是,用推导式的形式写,把外层括号改成圆括号,产生的对象就变成了“生成器”对象,再用for .. in .. 循环取出作二次处理,图中例子 g = (i for i in range(10**(n-1), 10**n)) 就是产生“生成器”对象的语句,而下面 for element in g 就是从生成器g中逐一取出element的意思。(优化技能got √)

Q2:它是n位数,为什么要先把数字转换成字符串,而不是通过数学计算把每一位拆出来呢?

A:在这个具体问题以及答案中,把数字转换成字符串类型,可以利用Python中各种字符串处理方法的遍历,来解决判断是否出现“0”、是否出现重复数字位的问题,假设有字符串值的变量s,通过'0' in s便能直接判断s中是否有0;

而代码中出现的set(s)的形式,是把字符串s看做是一种特殊的list(列表),把它直接转换成一个集合对象,利用集合中的元素都唯一的特点,只要集合set(s)中的元素个数跟字符串s的长度数字一致,便说明字符串s中的每一位都是不重复的。(基础技能活用got √)

Q3:这段程序的思路很不错,代码上还有改进空间吗?

A:还是有的,按照思路,最终生成结果放到list中,可以改进为直接用列表推导式把结果list生成出来,再比如把具体计算输出过程包装成一个函数,把输入部分包装到主程序中,再增加异常处理,等等;这里篇幅关系暂时不讲列表推导式,对它有疑问的朋友,可以在评论区里提出。(经验积累学习方式got √)

经过改进,新的代码答案如图。

02

思路2:“递归”算法

这一段是笔者提供的代码答案,如图。

笔者的思路是这样的:

① 用户输入n;

② 把n传入具体计算结果的函数,在函数内判断n是否符合要求,不符合则抛出错误;

③ 在函数中写出生成一个位数字的方法 for in range() ,利用“递归”算法,每进入一层函数,则生成的数字增加一位,到最后一层生成符合条件的n位数时,把这个n位数用print直接输出;

QA问答环节

Q1:isinstance()是什么?3

A:isinstance()是用来判断对象是否为指定类型的实例,简单来说,isinstance(n, int)是判断n是否为int(整数)类型,那么isinstance(s, str)便是判断s是否为str(字符串)类型,isinstance(d, dict)判断d是否为dict(字典)类型,以此类推;

在Python中,支持类似数学形式的条件判断,可以写成3

Q2:“递归”算法是什么?这个答案为什么可以这样写“递归”?

A:“递归”的概念很简单,写一个函数,让它能调用自身,一层一层地深入下去,这个函数就是递归函数,但是单纯让它调用自身,不是会陷入一个死胡同里面吗?所以“递归”算法重要的一点是,需要给它设计一个“停止条件”,让它在适当的时候停下来,完成我们需要的计算;另一点重要的是,设计它在每一层调用时做什么。

在这个具体例子里面,output函数有两个参数n和num,n是我们输入n后传入的,num是生成出来的数字,是字符串的形式,一开始什么数字都没有,所以初始值是''(空字符串);output(n, num+str(i)) 是调用自身的关键语句,这个递归函数每进入一层,数字就在最右边增加一位,停止条件就是当数字位数跟输入的n相等的时候,也就是 if len(num) == n 这句。

也就是说,我们把num看成一个箱子,可以放进数字的那种,假设n=3,一开始num='' 即箱子空空如也,每进入一层函数时,用循环 for i in range(1, 10) 每次把1到9中的某一个数字放进箱子 num+str(i),然后通过 output(n, num+str(i)) 这一句进入下一层,也是同样方式用循环 for i in range(1, 10) 每次把1到9中的某一个数字放进箱子……当放进箱子的数字数量是n的时候,就可以把这个数字输出了。这里需要留意的是,作了判断 if str(i) in num,如果数字i还没放进过箱子里,才把它放进去的。

编写递归函数应用“递归”算法时,特别需要留意上述的“停止条件”和“每一层调用时做什么”这两点,还是不太理解的朋友,可以在评论区里再次提出。(基础算法知识got √)

笔者认为,新手在学习时更应该关注解决问题的思路上,而非运行出来的结果,通过勤奋的练习和举一反三的学习方式,与高手之间的差距便能很快地缩短。

· (作业题) 编写一个函数,实现把一个字符串中的字符(ASCII可见字符范围,如字母A-Z、a-z、0-9等)循环右移n位,在主程序中实现输入输出。

输入:一个字符串,以及右移位数n (例如'abcdefghi',n=2)

输出:一个字符串(上面输入对应的结果为'hiabcdefg')

希望本篇可以给感兴趣或者想要学习的朋友们带来帮助哈。

有其他疑问也请在下方留言提出,会尽力解答~

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180627G0CZQ800?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券