如何修改Lisp的make-stack过程以支持空列表初始化?
支持空列表初始化的栈实现
原代码没法用空列表初始化,根源是直接操作传入的列表——空列表不是cons单元格,没法执行car、set-car!或set-cdr!这类操作。换个思路,用一个容器把栈内容包起来,就能解决这个问题:
(define (make-stack lst) ;; 用单元素列表当容器,存当前栈的实际内容 (define stack-box (list lst)) (lambda message (case (car message) ((see) (newline) (write (car stack-box))) ((empty?) (null? (car stack-box))) ((push) (let ((new-top (cadr message))) ;; 新元素压栈,直接构造新的cons结构当新栈 (set-car! stack-box (cons new-top (car stack-box))))) ((pop) (if (null? (car stack-box)) (error "空栈不能弹出元素") (let ((elt (car (car stack-box)))) ;; 弹出后,栈变成原栈的剩余部分 (set-car! stack-box (cdr (car stack-box))) elt))))))
核心改动
- 新增
stack-box容器:不管栈空不空,这个容器都是有效的cons单元格,能安全执行set-car!操作 push操作直接用cons构造新栈,替代原代码里绕弯的手动修改逻辑(原逻辑在空栈时直接失效)pop操作先检查栈是否为空,避免报错;弹出逻辑更直观,直接取栈顶、更新栈为剩余元素- 所有访问栈内容的地方都指向容器里的
(car stack-box),统一操作入口
测试示例
现在可以放心用空列表初始化了:
;; 空栈初始化 (define s (make-stack '())) (s 'empty?) ; 输出 #t ;; 压入元素 (s 'push 10) (s 'push 20) (s 'see) ; 输出 (20 10) ;; 弹出元素 (write (s 'pop)) ; 输出 20 (newline) (s 'see) ; 输出 (10) (write (s 'pop)) ; 输出 10 (newline) (s 'empty?) ; 输出 #t ;; 空栈弹出会报错 ;; (s 'pop) ; 提示:空栈不能弹出元素
原代码失效原因
原代码的push试图直接修改传入列表的cdr,把原列表改成(新元素 原元素 . 原cdr)的结构——这其实是手动模拟cons,但如果传入的是空列表:
set-cdr!会报错,因为空列表不是cons单元格,没法修改它的cdrpop时取(car lst)也会报错,因为空列表没有car
用容器封装后,我们操作的始终是容器里的栈内容,彻底避开了空列表的操作限制。
内容的提问来源于stack exchange,提问作者david
相关产品推荐
相关产品推荐

