段敬利 ,张春飞,魏久鸿
C语言是大学教育的必修课程之一。目前很多C语言教学中的上机练习系统和上机考试系统一般都包括填空、改错和程序设计3部分。填空和改错这样的客观题目的自动评分技术比较成熟[1],但是对于主观题的阅卷技术却一直停滞难前[2]。目前主要采用的是结果对比评分法,即依据程序运行结果和标准答案对比给出相应分数,结果正确得满分,不正确得零分。这种方法衡量的只是一个输出文件或者输出结果,完全忽略了考生的源代码,显然有失公允,不能反映学生的真实水平。而且现有的考试系统多为单机版和基于C/S模式的,存在时间和空间受限的缺点。
针对上述问题,本文研究了一套基于Web的C语言考试及自动评分系统,该评分系统充分考虑学生的源代码,在结果对比评分的基础上,给出了修复编译、代码对比相结合的评分方法。
C语言教学中上机考试及评分系统主要包含3部分:考试系统、试卷管理系统和评分系统,如图1所示。考试系统采用基于Web的B/S模式,只有服务器端,客户端为Web浏览器。服务器其实是一个网站,管理员也可通过Web浏览器对数据库进行维护。而学生也是通过浏览器进行登录、试题抽取和提交考试结果。本系统学生提交的不是文件,而是以表单的形式把学生的答案提交到数据库对应的字段,评分系统提取学生答案,再组成C文件进行评分,考生在线考试过程如图2所示。
图1 功能模块图
图2 在线考试过程
C语言教学中上机考试及自动评分系统采用微软的SQL Server作为系统数据库,在Windows XP操作系统、IIS服务器和Asp.net环境下设计动态网页,实现考试系统。自动评分系统,则在Visual C++.net环境下实现。
评分系统的主观题采用结果对比、修复编译与代码对比相结合的评分方法。结果对比是检查考生程序的运行结果是否与标准答案的运行结果一致。修复编译是修改源文件中的语法错误,使程序生成EXE文件,再进行结果对比评分。代码对比评分是将规范化处理后的学生程序与标准答案知识要点进行相似度计算,得到各知识点分值,再累计得到总分,它类似于教师在评阅主观题时的思维方法。系统评分方法如图3所示。
图3 系统评分方法图
程序设计题评分之前的一项重要工作是保存程序设计题的各知识要点和评分标准。由于是课程考试,给出的程序不会很复杂,先分析程序设计题并设定知识要点及其分值,一道试题的各知识要点的分值之和等于该试题的分值,将每道程序设计题的知识要点、分值分别保存在知识要点评分表answerkey的 q_answerkey和 q_keyscore字段中。程序设计题的评分过程分为以下4个步骤:
a.排除未答题考生。
先判断考生程序设计题答案字段stu_answer是否存在,若不存在,则得0分,存入试题得分表detailscore中的e_score字段中;若存在,则转到评分模块进行评分。用伪代码表示如下:
if stu_answer exist then
e_score=0
else go to评分模块
b.结果对比。
结果对比是检查考生程序的运行结果是否与标准答案的运行结果一致,若一致,则可得满分,并将得分存入detailscore表(各试题得分表)中的e_score字段中;否则转到代码对比评分。
c.修复编译。
即使学生思路明确,程序中也可能出现语法错误,致使程序不能产生运行结果。修复编译的作用是修改学生程序中的语法错误,进而使程序能通过编译和链接,生成EXE文件和答案。但是每修改一次语法错误要从该题目总分中扣除相应的分数,比如总分100分,一次修改扣10分的话,经过一次修改生成EXE文件,并且输出和标准答案相同的结果,则可得到90分。如果修改的次数超过规定次数,则进入代码对比评分。常见的语法错误主要是标点符号的遗失和输入错误的标识符。下面简要概述修复编译的过程和关键技术。
首先对源程序中的单词进行词性分析,词性分析的主要任务是识别各个词汇的类型的值,识别后的各个词的类型保存在一个链表word_list中。词性分析过程中用到的结构体定义如下:
struct word
{
char lab_name;//标识类型
int state;//状态
int number;//序号
int line;//行号
char name[50]//词汇
struct word*next;//指向下一节点的指针
}
对程序扫描后,各个单词保存在各自的结构体变量中,并生成链表word_list,供语法分析使用。
然后对word_list链表进行语法分析,如不合法,则判断错误位置,并尝试修正。如果是标点符号遗漏,则还需要判断遗漏的是何种标点,然后在遗漏位置插入相应的标点符号。如果发现未定义的标识符,则查找本程序中已定义的标识符表和关键字表,如发现相似度较高的单词,则进行替换。经过上述步骤,可修改程序中的一处语法错误,然后进行下一次编译。
d.代码对比评分。
代码对比评分是在评分系统的题库中事先存放不同算法的正确关键代码,评分的时候系统通过对比学生程序中这些关键代码,给出相应的分数。但是,同样一个程序设计题目,同样的算法,其书写的方法和格式也是千变万化的,而且标识符的定义也是由个人喜好决定。用简单的、死板的关键字对比,显然是行不通的。这里采用正则公式[3]来应对C语言的语法特性。
如求10的阶乘的程序关键代码如下:
int a=1,i=1;
while(i<=10){a=a*i;i++}
其中存放结果的变量a和循环控制变量i,不同的学生会用不同的名字来命名,那么可用一种通用的表示方法来表示这段代码:
int@VAR@=1,@VAR@=1;
while(@VAR@<=10){@VAR@=@VAR@*@VAR@;@VAR@++}
这里可以用C语言变量的正则表达式“(s)*(w)+(s)*”对上面的代码进行匹配。
评分系统首先用正则公式对学生代码进行规范化处理,然后根据知识要点评分表中给出的知识要点和评分标准,评判程序对知识要点的覆盖情况,计算学生代码和各知识要点的相似度值,分别存入知识要点得分表中的e_keyscore字段。最后计算各知识点得分的总和,存入该题总分(e_score)字段中。
本文所提出的C语言程序设计题目的评分方法,结合了几种不同的评分方法,取长补短,弥补了现有评分方法的不足,避免了出现一点错误即得零分的不合理现象,可以更客观地反映学生的真实水平,增强了考试的公平性和真实性。实践证明,采用上述评分方法的考试系统适合用于C语言教学中的上机练习和考试。本文提出的修复编译还仅限于标点符号遗失和标识符错误的修复,虽然不能完全替代教师的阅卷工作,但却是一种充分考虑学生源代码的新思路,为研究出更科学的评分方法提供了依据。
[1] 孟爱国,卜胜贤,李鹰,等.一种网络考试系统中主观题自动评分的算法设计与实现[J].计算机与数字工程,2005(7):147-150.
[2] 王永生.计算机阅卷中主观题型的单层模糊综合评判[J].青海大学学报,2000(3):46-49.
[3] 佛瑞德(Jeffrey Friedl E F).精通正则表达式[M].3版.余晟译.北京:电子工业出版社,2012.