You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 , found row(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, ...)格式的复合项,不是普通列表,不能用列表匹配的方式解构取值。
修复方案

只需要修改两处核心错误即可:

  1. 把Grade is grade(Score)替换为正确的谓词调用grade(Score, Grade),去掉错误的is
  2. 保留最初的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 09:48:27