R5RS Scheme中可变对象求和时caddr行为异常的原因咨询
嘿,我来帮你捋清楚这个问题~
首先,你遇到的这个矛盾现象(单独调用(caddr lst)返回3,但和+结合就返回奇怪的mcons列表),核心原因大概率是你在循环/递归的过程中,没有正确控制要处理的列表元素,或者误把整个列表对象当成数值传给了+。
举个例子,假设你测试(caddr lst)时,用的是完整的初始列表(比如'(1 2 3 4 5)),这时候(caddr lst)确实会返回3——但在你的求和代码里,随着循环推进,lst可能已经被修改成了更短的列表(比如'(2 3 4 5)甚至更短),这时候如果还是硬套(caddr lst),要么取到的不是你想要的数值,要么当列表长度不足3时,某些Scheme实现不会直接报错,反而返回剩余的列表结构(虽然这不符合R5RS的严格标准)。更有可能的是,你在代码里不小心把(cdr lst)(剩余列表)当成了数值加到总和里,比如写了(+ x (cdr lst))而不是(+ x (car lst))或者(+ x (caddr lst)),这时候+会错误地把数值和列表做了类似cons的操作,就出现了你看到的mcons开头的奇怪结果。
既然你想用可变对象(配合let和set!)来实现列表求和,我给你写个标准的迭代版本参考:
;; 基础版本:对整个列表求和 (let ((lst '(1 2 3 4 5)) (sum 0) (current lst)) ; 用current跟踪当前要处理的列表部分 (let loop () (if (not (null? current)) (begin (set! sum (+ sum (car current))) ; 每次取当前列表的第一个元素 (set! current (cdr current)) ; 移动到下一个元素 (loop)) sum))) ; 循环结束后返回总和
如果你是想从列表的第三个元素开始求和(也就是用到caddr的场景),可以改成这样:
;; 从第三个元素开始求和 (let ((lst '(1 2 3 4 5)) (sum 0) (current (cddr lst))) ; 直接跳到第三个元素开始处理 (let loop () (if (not (null? current)) (begin (set! sum (+ sum (car current))) (set! current (cdr current)) (loop)) sum)))
简单说,你需要确保每次加到总和里的是单个数值(比如(car current)),而不是整个列表对象。单独测试(caddr lst)没问题,是因为你用的是初始的完整列表,但代码运行时列表已经在动态变化了,这就是问题的关键~
内容的提问来源于stack exchange,提问作者Artemis

