Racket for循环报application: not a procedure错误排查与实现
Racket通用CSV转Prolog子句实现方案
报错根因
触发application: not a procedure; expected a procedure that can be applied to arguments错误的核心原因是代码括号匹配/迭代逻辑错误:
- Racket为前缀语法,括号位置错误会直接改变执行逻辑:你写的嵌套循环大概率在按索引取谓词、取单元格值时多写/漏写括号,把从列表中取出的字符串、符号类值(比如谓词名、单元格内容)放在了过程调用的位置,Racket尝试将这些非过程值当函数执行,直接抛出该错误。
- 普通嵌套
for循环会生成两个列表的笛卡尔积,不符合“每行单元格与同位置谓词一一匹配”的需求,且基础for迭代不会自动累积生成的Prolog子句到结果列表中。 - 原有逐列手写
when判断的硬编码方案与CSV表头结构强耦合,列名、列数、列顺序调整时必须修改源码,编码量大且极易出现拼写错误。
实现思路
完全解耦CSV结构与代码逻辑:
- 先读取CSV第一行作为表头,自动生成/匹配每一列对应的Prolog谓词列表,和列位置一一对应
- 逐行遍历数据行,并行遍历当前行的单元格与对应位置的谓词,自动生成Prolog子句
- 统一处理Prolog语法格式(特殊原子加引号、数字直接输出)
- 所有子句生成完成后一次性写入目标pl文件
样例参考
- 测试用women.csv内容:
name,age,occupation,nationality Alice,30,teacher,American Bella,25,designer,Canadian
- 期望输出的females-by-Racket.pl内容:
name(alice, 'Alice'). age(alice, 30). occupation(alice, teacher). nationality(alice, 'American'). name(bella, 'Bella'). age(bella, 25). occupation(bella, designer). nationality(bella, 'Canadian').
可直接运行的通用代码
依赖Racket官方csv-reading库,可通过raco pkg install csv-reading安装:
#lang racket (require csv-reading) ;; 路径配置 (define input-csv-path "women.csv") (define output-pl-path "females-by-Racket.pl") ;; 自定义谓词映射:键为CSV表头名,值为对应Prolog谓词名,未配置的列默认用表头小写作为谓词 (define custom-predicate-map (hash)) ;; 读取CSV内容 (define csv-reader (make-csv-reader (open-input-file input-csv-path))) (define all-rows (csv->list csv-reader)) (define header-row (first all-rows)) (define data-rows (rest all-rows)) ;; 生成与列位置一一对应的谓词列表 (define predicate-list (for/list ([col-name (in-list header-row)]) (string->symbol (hash-ref custom-predicate-map col-name (string-downcase col-name))))) ;; 值转合法Prolog项:数字直接输出,纯小写原子直接输出,其他内容加单引号包裹 (define (to-prolog-term val) (cond [(string->number val) val] [(regexp-match #rx"^[a-z][a-zA-Z0-9_]*$" val) val] [else (format "'~a'" val)])) ;; 生成所有Prolog子句 (define all-clauses (for*/list ([row (in-list data-rows)] ;; 跳过全空的无效行 #:when (not (andmap string-empty? row)) ;; 默认第一列为姓名,转小写作为事实的第一个参数 [person-id (in-value (to-prolog-term (string-downcase (first row))))] ;; 并行遍历谓词列表和当前行单元格,一一对应不生成笛卡尔积 [(pred cell-val) (in-parallel (in-list predicate-list) (in-list row))]) (format "~a(~a, ~a)." pred person-id (to-prolog-term cell-val)))) ;; 写入结果到Prolog文件,存在则覆盖 (with-output-to-file output-pl-path (lambda () (for-each displayln all-clauses)) #:exists 'replace)
使用注意事项
- 并行遍历两个等长列表必须使用
in-parallel,否则会出现笛卡尔积错误,生成大量不符合预期的子句。 - 如果第一列不是姓名字段,只需修改
person-id的取值逻辑即可,不需要改动其他遍历代码。 - 如果CSV列数变动、列名调整,不需要修改核心遍历逻辑,只需在
custom-predicate-map中补充特殊列的谓词映射即可。 - 写代码时可借助DrRacket的括号高亮功能检查括号配对,从根源避免“值被当过程调用”的语法错误。
内容的提问来源于stack exchange,提问作者John Smith
相关产品推荐
相关产品推荐

