「BUAA OO Unit 1」 第一单元测试与数据构造

Posted by saltyfishyjk on 2022-04-15
Words 1k and Reading Time 4 Minutes
Viewed Times

「BUAA OO Unit 1」 第一单元测试与数据构造

Part 0 前言

笔者在第二次研讨课上就第一单元测试与数据构造这一主题进行分享,这里记录课上分享的思路与内容。

构建不依赖路径的jar包、构建不依赖具体测试要求的自动化评测机框架、构建数据生成机。这三者相互解耦合,利于根据作业需求的迭代对评测机快速迭代。

以下以第一次作业评测机搭建为例,并将在Part3部分介绍本设计对迭代开发的支持。

Part 1 获取jar包

为了避免不同同学的Java项目结构目录、依赖包等的不同导致的无谓的麻烦,将Java项目打包为jar包,方法可移植性强。一种简单的获取jar包的方式可以见面向测试小白的简易评测机中的Part 2

Part 2 自动化评测机框架

注:以下为python代码,并且需要python3.5以上版本以支持相应库

命令行运行jar包并重定向输入和输出

1
2
3
4
5
6
7
8
import subprocess
from subprocess import STDOUT, PIPE

def execute_java(stdin):
cmd = ['java', '-jar', 'homework.jar']# 更改为自己的.jar包名
proc = subprocess.Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
stdout, stderr = proc.communicate(stdin.encode())
return stdout.decode().strip()

判定输出答案正确性

利用sympy处理

1
2
3
4
5
6
7
8
poly = genData() # genData提供输入数据,将在下一Part中介绍。
f = sympy.parse_expr(poly) # 获取标准答案
strr = execute_java(poly) # 获取输出答案
g = sympy.parse_expr(strr)
if sympy.simplify(f).equals(g) : # 判定输出答案和标准答案是否相同
print("AC : " + str(cnt))
else:
print("!!WA!! with " + "poly : " + poly + " YOURS: " + strr)

Part 3 数据生成

在Part 1中,我们利用了genData()方法获取poly,该模块的一种实现思路如下:

分层构造数据

依照Expr->Term->Factor的思路,构建如下模块:

getExpr()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import random
import sympys

def getExpr():
index = random.randint(indexA, indexB) # 设置表达式指数
termNum = random.randint(termNumA, termNumB) # 设置该Expr包含的term个数
str = ""
for i in range(termNum):
whiteNum = random.randint(whiteA, whiteB)
str = str + getTerm() + getWhite(whiteNum)
withIndex = random.randint(0,1)
if withIndex == 1:
str = "(" + str + ")**" + index
return str

getTerm()

1
2
3
4
5
6
7
8
9
10
11
def getTerm():
str = ""
sigNum = random.randint(0,2) # 设置项的符号个数
for i in range(sigNum):
if(random.randint(0,1) == 1):
str = str + "+"
str = str + "-"
factorNum = random.randint(factorA, factorB)
for i in range(factorNum) :
str = str + getFactor()
return str

getFactor()

1
2
3
4
5
6
7
8
9
10
def getFactor():
str = ""
type = random.randint(1,3)
if type == 1:
str = getNum()
elif type == 2:
str = getPower()
elif type == 3:
str = getExpr()
return str

getNum()

1
2
3
4
5
6
7
8
9
def getNum():
str = ""
pos = random.randint(0,1)
if pos == 0:
str = str + "-"
else:
str = str + "+"
str = str + random.randint(A,B)
return str

getWhite

1
2
3
4
5
6
7
8
9
def getWhite(ct): # ct控制生成空白字符数
str = ""
for i in range(ct):
flag = random.randint(0, 1) # 随机控制传入的是空格还是table
if flag == 0:
str = str + ' '
else:
str = str + '\t'
return str

getPower()等函数不再枚举,与之类似

Part 4 迭代开发

以上简述了对于第一次作业的评测机实现的部分细节,以下简要介绍如何针对第二三次作业的新需求迭代开发我们的评测机

新因子

对于sincos,直接利用形式化表达的表述,构建getTri()方法,并将其扩充入getFactor()

自定义函数

设计getSelfDefineFunc()方法,并将其扩充入getFactor()。同时还需要设计构造自定义函数定义式。

sum

构造思路同自定义函数,需设计getSum()

复杂度控制

通过在getExpr()等方法内设计indexName等变量,控制随机数生成范围,保证不超过形式化表达要求。

边界情况讨论

设计常量池,包含如1,-1,2,-2,0,2147483647,-2147483647等边界数据和易化简出错数据,同时根据需求扩充常量池。

嵌套

由上述介绍,得益于我们依照形式化表达构建数据生成器,我们可以根据其解耦合的特点在getFactor()等模块加入其他新增可作为因子的生成器。

Part 5 组装

Part0-2介绍的三个模块具有高内聚低耦合的特点,只需要简单调用彼此即可组装完成。得益于这样的特点,我们可以快速迁移本自动化评测机。


This is copyright.