SWI-Prolog读取CSV计算学生成绩时报类型错误如何解决
问题描述
我尝试读取如下格式的CSV文件:
NO,ID,Name,HW1,HW2,HW3,Midterm,Final 1,410021001,Alan,90,84.5,117,60,66 2,410021002,Bob,85,49,80,57,64 ..............................
实现代码如下:
%This is a prolog version of _410921334_hw2.py % 从HW2data.csv读取数据,计算每个学生的总评成绩 % 逐行读取文件,第一行为表头 % 文件结构:序号,学号,姓名,作业1,作业2,作业3,期中成绩,期末成绩 % 所有成绩先四舍五入为整数,再按权重计算总评 % 总评权重:作业1*0.1 + 作业2*0.1 + 作业3*0.1 + 期中*0.3 + 期末*0.4,结果四舍五入为整数 % 输出格式:学号 姓名 总评 等级 % 等级对应规则:A+ = 90-100, A = 85-89, A- = 80-84, B+ = 77-79, B = 73-76, B- = 70-72, C+ = 67-69, C= 63-66, C- = 60-62, D = 50-59, E = 0-49 :- use_module(library(csv)). test :- augment('HW2data.csv', 'test.out.csv'). augment(InFileName, OutFileName) :- csv_read_file(InFileName, [_|Rows], []), open(OutFileName, write, OutStream), write(OutStream, 'StudentID,Name,Score,Grade'), nl(OutStream), augment(Rows, OutStream), close(OutStream). augment([], _). augment([row(_, ID, Name, HW1, HW2, HW3, Midterm, Final)|Rows], OutStream) :- Score is round(HW1 * 0.1 + HW2 * 0.1 + HW3 * 0.1 + Midterm * 0.3 + Final * 0.4), Grade is grade(Score), write(OutStream, ID), write(OutStream, ','), write(OutStream, Name), write(OutStream, ','), write(OutStream, Score), write(OutStream, ','), write(OutStream, Grade), nl(OutStream), augment(Rows, OutStream). grade(Score, Grade) :- Score >= 90, !, Grade = 'A+'. grade(Score, Grade) :- Score >= 85, !, Grade = 'A'. grade(Score, Grade) :- Score >= 80, !, Grade = 'A-'. grade(Score, Grade) :- Score >= 77, !, Grade = 'B+'. grade(Score, Grade) :- Score >= 73, !, Grade = 'B'. grade(Score, Grade) :- Score >= 70, !, Grade = 'B-'. grade(Score, Grade) :- Score >= 67, !, Grade = 'C+'. grade(Score, Grade) :- Score >= 63, !, Grade = 'C'. grade(Score, Grade) :- Score >= 60, !, Grade = 'C-'. grade(Score, Grade) :- Score >= 50, !, Grade = 'D'. grade(Score, Grade) :- Score < 50, !, Grade = 'E'. grade(_, Grade) :- Grade = 'Error'. % 运行入口:?- test.
运行后报错:
Type error: file_name_extension/3
character_code' expected , foundrow(1, 410021001, 'Alan', 90, 84.5, 117, 60, 66)' (a compound)
程序可以正常识别文件、跳过表头、写入输出文件表头,但处理第一行数据时就触发上述错误。我作为Prolog新手,曾尝试把读取代码改成csv_read_file(InFileName, Rows, []),同时把行匹配逻辑改成列表解构:
augment([], _). augment([Row|Rows], OutStream) :- Row = [_, ID, Name, HW1, HW2, HW3, Midterm, Final],
问题仍然存在,需要定位错误原因并给出修复方案。
错误原因
这个报错是两个基础Prolog语法错误触发的连锁反应,和文件读取逻辑本身无关:
is/2谓词滥用:Grade is grade(Score)是完全错误的写法。SWI-Prolog中is/2的右侧只能是合法算术表达式,grade/2是自定义的关系谓词,不是内置算术函数,不能放在is右侧调用。Prolog解析这个非法表达式时,会错误地把读取到的row(...)复合项传入底层字符/文件名处理逻辑,才抛出了看起来和文件操作相关的迷惑性报错。- 谓词参数不匹配:定义的
grade是接收两个参数的谓词grade(Score, Grade),调用时只传了一个参数,本身就不符合Prolog谓词调用规则。 - 后续尝试的列表解构写法也是错的:
csv_read_file/3返回的每一行是row(字段1, 字段2, ...)格式的复合项,不是普通列表,不能用列表匹配的方式解构取值。
修复方案
只需要修改两处核心错误即可:
- 把
Grade is grade(Score)替换为正确的谓词调用grade(Score, Grade),去掉错误的is - 保留最初的
row(_, ID, Name, HW1, HW2, HW3, Midterm, Final)复合项匹配逻辑,不要改成列表解构写法。
修复后处理单行数据的代码片段如下:
augment([row(_, ID, Name, HW1, HW2, HW3, Midterm, Final)|Rows], OutStream) :- Score is round(HW1 * 0.1 + HW2 * 0.1 + HW3 * 0.1 + Midterm * 0.3 + Final * 0.4), grade(Score, Grade), % 修正调用方式 write(OutStream, ID), write(OutStream, ','), write(OutStream, Name), write(OutStream, ','), write(OutStream, Score), write(OutStream, ','), write(OutStream, Grade), nl(OutStream), augment(Rows, OutStream).
额外注意:如果CSV中的数值字段被读取为字符串,可以在csv_read_file/3的选项中添加convert(true),让库自动完成数值类型转换,避免算术计算报错,对应写法为csv_read_file(InFileName, [_|Rows], [convert(true)])。
内容的提问来源于stack exchange,提问作者Sifiso Lucolo Dhlamini
相关产品推荐
相关产品推荐

