关于实验考试的提示
关于斩杀
需要各位同学注意的是,实验考试按照历年的情况是有斩杀线的!也就是说,考生必须完整得到一道题的分数,即在一道题上获得“答案正确”和红色对勾,否则就会不幸挂科. 当然,在当天下午会安排一次重考机会. 希望同学们认真对待实验考试,因为斩杀是系统上完成的,老师和助教都无法更改,虽然我们不希望任何一位同学挂科,但这种情况的挂科确实无法可想. 因此,请同学们认真对待实验考试,充分复习,并且搞明白任何一个不明白的知识点,如果搞不明白可以向助教和老师提问,我们会尽可能及时回答.
程序设计方法论
一般地,关于程序设计的题目,我们需要按照下面的几个步骤完成. 其中有些步骤是可以跳过的,但是跳过任何一个步骤都有可能提升调试时的心智负担:
- 仔细阅读题目,阅读关于输入和输出的样例.
- 描述程序执行时的步骤,画出程序执行时的流程图. 这一步对于一些简单的程序可以在脑海中完成,但是对于有分支、循环之类的控制流语句的时候,不太熟悉的读者还是应该仔细完成. 此外,如果在提交调试时出现问题,那么读者哪怕觉得自己经验足够丰富,也最好重新完成这一步,并且对照预期的和实际程序执行的结果.
- 将流程图转化成程序语言. 此时,读者需要注意的是编程时的缩进. 需要指出的是,
for ... else ...
语句的缩进有可能会导致直观性不足,如非必要不建议使用. - 在下面的框中测试样例的输出. 注意,输入测试样例 这样的提示词表明你需要自己复制样例,或者程序没有输入. 需要注意输入和输出的格式,尤其是空格等等. 此时,建议读者将样例输出直接复制进行修改,以防出现各种各样的格式问题.
- 在测试完成之后,可以进行提交,如果提交通过,即可进行下一题,以下的步骤都是提交不通过或者样例测试不通过时的操作.
- 对于有一部分输入非零返回,或在直接测试样例时就会出现
Traceback
以及报错信息. 此时,触发每一个可能的分支,观察报错信息的行数以及内容. 这是需要在平时的编程实践中积累的内容. - 对于样例答案错误,或者部分情况答案错误时,测试边界条件,并且在每个位置检查程序输出与预期输出不符的来源,这在下面关于调试的章节会进一步讨论. 尤其要注意的是一些报错情况的输出. 在出现多个
print
语句时,一定要检查各个输出是否符合预期,尤其是一些文本形式的输出,检查大小写、中英文符号等问题,这是非常关键的一点. - 对于运行超时,需要检查是否陷入死循环. 另外,如果对于某些输入运行超时,常见的情况是在检查一个数
x
是否为素数时,只需检查到x ** (0.5) + 1
的范围即可. 类似地,也需要检查循环的范围是否太大,等等. 这是不太可能出现的情况. - 格式错误,注意输出语句中的空格(尤其是最后的空格是否存在). 这时,最好复制题给的预期输出到代码方框中,观察是否有
·
,这是代码方框中对空格的标识. - 如果发现自己确实无法可想,并且完成了上面的所有步骤,尽快跳到下一道题,以防出现卡死在一道题上,导致整场考试没有完整完成任何一道题的情况!!!
程序的测试
- 如果出现非零返回,仔细阅读报错信息和报错的部分代码,按照提示解决问题.
- 如果出现答案错误,自己设计输入进行测试,可以充分使用 PTA 所给的测试区解决问题.
- 如果有一部分答案错误,请考虑代码可能出现的边界条件(例如某些分支、循环第一项的问题等等).
- 在发现错误之后,可以通过在代码中输入
print
语句缩小可能出现的错误代码范围. 注意,PTA 的测试区的机制是如果有报错信息就不会输出任何标准输出的内容,因此如果有报错请注释掉报错的语句,并在报错来源之前进行输出检查报错原因.
基本调试方法
对程序的错误的调试方法核心就是分而治之.
首先,明确何时会发生错误. 这需要自己构造一些运行样例,并且找到与预期不符的可能. 一般情况下,需要对循环语句和分支语句尤其谨慎,测试时应尽可能使得所有分支都被覆盖,并且注意循环的首项等特殊情形.
在明确何时发生错误之后,需要对此时的错误输出进行分析. 通过在代码中插入 print
语句来检查在何时哪个变量发生了意料之外的改变. 注意在 print
的输出中字符串列表和数字列表的区别,以及嵌套列表等等信息. 在不确定的情况下,可以使用 type
函数的返回值来判断类型是否正确. 通过 print
的输出对错误输出的产生过程进行追溯,从而确定到某个语句或者某个条件的判断是否准确.
在精确到某个语句的错误之后,可以通过改写等方式修改此语句与预期不符的执行结果的来源. 通过这样的方法,应当能够使得结果与预期相符. 接下来反复此过程,直到程序输出完全正确.
易错点复盘
下面是对可能的易错点进行的一些总结:
类型错误
类型错误是很常见的问题之一. 一般情况下,非零返回时的信息会告诉你类型何时为何发生错误. 这时的修改方法往往是显然的,但也会有一些不那么显然的类型错误:
a = input()
if a == 0:
print("Zero!")
else:
print("Nonzero!")
这时,不会触发非零返回. 但是,一些测试会表明,哪怕输入了 0
,程序也会输出 Nonzero!
. 这显然是不符合预期的. 其原因是在判断语句中的类型错误:字符串 "0"
和整数 0
的比较显然是不相等的,但是它们确实可以比较. 这是面对非预期结果需要注意的一类重要问题.
格式错误
这里所称的格式错误并不止表示 PTA 告诉你的格式错误. PTA 所称的格式错误仅限于空白符相关的错误,通常会遇到的情况都是空格. 当然,有兴趣的读者可以检查有无空格时的预期输出格式等等信息,这在笔者看来往往还是比较明显的.
另一种格式错误包括大小写、标点问题,此时 PTA 的提示会是答案错误. 因此,遇到答案错误时,需要做的第一步往往是检查输入输出中是否存在标点、大小写之类的明显问题,这往往还是容易发现的,但对于不注意的学生而言则会成为半小时以上的折磨的源泉. 一个技巧是,永远要从输出样例中复制字符串并进行修改. 这样,你能够尽可能避免自己输入时出现的问题,最大程度减少这类问题发生的可能.
题意理解错误
因为出题老师的语文水平所限,出题时常常会有一些模棱两可的情况出现. 此时,最好的方法是自己观察输入输出的样例是否与题意匹配,如果不匹配,尝试从别的角度理解题意,如果还是无法理解,可以举手求助.
当然,也有一些关键词往往会成为老师挖坑的陷阱,这种题目通常非常无聊且不道德:
- 输入的“元素”不一定是整数,通常如果不直接要求计算,当成字符串处理会是更加通用的;如果要求计算,使用
float
也会比使用int
更为通用——当然,也要视情况而定. - 题目通常会出现一些输出格式的限制,例如保留几位小数,是否区分大小写等等. 这些情况下一般会用字符串方法(包括格式化字符串)来处理,不熟悉的读者需要多加留意.
- 有时题目对输出格式没有说明,此时需要参考输出样例的输出方法. 很多时候,出题人总会加上一些毫无意义的提示词,这也会出现上面所谓的格式错误的问题.
一些上课可能不会告诉你的小技巧
help
函数和 dir
函数
可以使用 help
函数检查某些函数的说明. 例如,可以通过输出
help(input)
来查看 input
函数的说明. 对于一些方法(需要加 .
调用的函数),需要用 类型.方法名
的方法查看,例如:
help(str.isalnum)
也可以通过 dir
函数来查看一个类型所有的方法,例如:
dir(str)
或者查看某个库中所有的内置函数:
import math
dir(math)
实际上也可以使用 help
,但是输出会很长:
import math
help(math)
组合使用这两个函数可以让读者在没有办法翻书的情况下理解大部分做题所需使用的内置函数和方法.
exit
函数
这个函数是出于一些对 PTA 的邪道利用. 这有点类似于猜测测试样例,偶尔在出题老师比较缺德的时候会有奇效. 我们通常用的是 exit(0)
和 exit(1)
. 当程序执行到 exit(0)
时,程序一定会直接终止,并且不会执行此后的语句;当程序执行到 exit(1)
时,程序会出现非零返回. 通常情况下:
- 在某些测试点出现非零返回时,可以通过在程序中的某些位置插入
exit(0)
来测试这个测试点出现的非零返回的位置. 这是因为如果执行了exit(0)
,那么程序就会退出并且报答案错误. 通过找到答案错误和非零返回的分界点,可以确定在哪条语句发生了非零返回,并针对性地猜测可能出现的错误以及可能的错误输入. - 同理,在保证程序能够正常执行的情况下,可以通过
exit(1)
的插入来检查哪个分支被出现答案错误的样例执行了,进而猜测可能的错误输入是什么.