Scheme购物车计价程序调用add-cart报contract violation错误求解
问题原因
你的代码报错核心是对Scheme中quote(即语法里的单引号')的求值规则理解有误:
- 你调用时传入的
'(milk chicken carrots)是符号字面量列表,等价于(list 'milk 'chicken 'carrots)。单引号包裹的结构不会对内部标识符做求值替换,列表里存的是milk/chicken/carrots三个符号本身,根本不是你之前用define绑定的2.99、7.99、1.99这些数值。 - 你写的
add-cart函数默认列表元素直接是数字,直接取(car x)传入加法运算,递归到最后一个元素时拿到的是符号carrots,不满足+运算符要求参数为数字的契约,因此触发报错。
修复方案
不推荐用顶层define零散绑定商品价格的写法,这种方式耦合度高、扩展性差,也不方便做商品名到价格的映射查询。更符合Scheme习惯的实现是用关联列表(alist)统一存储商品价格映射,代码如下:
#lang scheme ;; 关联列表存储商品符号与对应价格的键值对 (define price-table '((milk . 2.99) (chicken . 7.99) (carrots . 1.99))) ;; 辅助函数:根据商品符号查询对应价格,不存在则抛出错误 (define (get-price item) (cond [(assq item price-table) => cdr] [else (error "无效商品名:" item)])) ;; 修正后的计价递归函数 (define (add-cart cart) (if (empty? cart) 0 (+ (get-price (car cart)) (add-cart (cdr cart))))) ;; 测试调用,传参格式和你原来的写法完全一致 (add-cart '(milk chicken carrots)) ; 运行返回正确结果12.97
如果你一定要保留最开始逐个define绑定商品价格的写法,理论上可以通过eval对取到的符号做顶层作用域求值来实现,但这种写法效率低、容易出作用域问题,完全不推荐在作业或正式代码里使用。
新手提示
- Scheme中单引号包裹的所有字面量都不会触发内部标识符求值,写的时候要明确区分「要被求值的变量」和「作为数据存在的符号/字面量列表」。
- 遇到
contract violation类的类型报错时,优先检查你传入运算的参数实际类型,顺着递归/调用链排查值的传递过程,很容易定位错位点。 - 存储键值映射类的数据优先用关联列表、哈希表这类专用结构,不要把数据零散绑定为顶层变量,后续增删商品、做优惠逻辑都会方便很多。
内容的提问来源于stack exchange,提问作者Margarita0076
相关产品推荐
相关产品推荐

