跳到主要内容

使用对拍器调试代码

· 阅读需 5 分钟

前言

做久了LeetCode,感觉快被LeetCode给惯坏了。LeetCode体验很好,不用处理输入输出,可以在线调试代码,出错时给出用例数据和正确结果。但是,很多OJ(比如HDOJ、ZOJ等)就只有极其有限的反馈。当好不容易写完代码,结果提交上去Wrong Answer,努力找代码中的错误也找不出来,这时该怎么办?

如果能找到AC的代码,那么,可以写一个测试用例数据生成器,然后将自己写的代码的运行结果与AC的代码的运行结果进行对比,这个过程叫“对拍”。对拍是在做OJ时很常用的手段,不过,往往需要进行多次的对拍,如果每次都手动执行对拍,无疑是件很繁琐的事情。如果一件事做起来很繁琐,那么就很难坚持下去,也难以享受其中的乐趣。所以,可以编写对拍器来自动进行对拍。

为了避免不必要的手动操作,我们希望对拍器能不断生成用例数据,直到自己写的代码的运行结果与AC的代码的运行结果不一致,最后自动打开文本比对工具进行结果比对。

代码

由于个人对Python较为熟悉,而且感觉用Python写这样的小脚本比较方便,所以就用Python写的对拍器作为例子。

from random import randint
import os

def gen():
pass

def run():
while True:
gen()
print('.', end='', flush=True)
cmd1 = 'wa.exe < in.txt'
cmd2 = 'ac.exe < in.txt'
ret1 = os.popen(cmd1).read()
ret2 = os.popen(cmd2).read()
if ret1 != ret2:
with open('1.txt', 'w') as f:
f.write(ret1)
with open('2.txt', 'w') as f:
f.write(ret2)
os.popen('meld 1.txt 2.txt')
break

if __name__ == '__main__':
run()

首先,需要一个生成用例数据的函数gen,它可以往in.txt写入随机生成的用例数据。cmd1和cmd2分别是运行自己的程序和AC的程序的命令,并把in.txt作为标准输入。接着会检查两个命令的运行结果,如果不一致就将运行结果写入文件,并打开文本比对工具进行比对。文本比对工具有很多,不过,个人觉得meld就还不错,免费,跨平台。这里需要将meld加入环境变量Path,或者写meld的全路径。

实战

今天在做ZOJ-2642时,题目并不难,感觉代码写得没问题,提交上去却Wrong Answer了。刚开始还以为ZOJ不支持STL,遂改成了不用STL实现,结果依然Wrong Answer。于是,使用对拍器进行对拍,发现进行了很多次对拍都没有出现和AC的代码的输出结果不一致的,这让我觉得有点沮丧。

最后,仔细阅读了题目,发现用例其实应该是由多组用例组成,但题目的示例里只给出了一组,这让习惯使用LeetCode的我有点不适应。意识到这个问题之后,遂果断加了层循环改成多组用例输入,结果对拍一次就出现了结果不一致。

仔细观察发现,只有第一个用例的结果是一致的,后面的用例的结果是不一致的。通过检查代码发现是滥用全局变量导致的,每次处理前,一些全局变量没有还原,导致使用的是上次的结果,所以就出现了只有第一个用例的结果是一致的情况。

另外,在没有AC的代码的时候,也可以写个暴力的解法来做对拍,从而发现代码里的错误,这在笔试时是一个不错的技巧。