发布的代码非常接近工作;正如 OP 帖子中所定义的那样,tsar 程序在要替换的项目是原子的情况下似乎可以正常工作,但当要替换的项目是列表时则不行:
> (tsar '(x (x) z) 'x '(y y))
((y y) ((y y)) z)
> (tsar '(x (x) z) '(x) '(y y))
(x (x) z)
大概这就是 OP 的意思,“...总的来说,我没有收到我想要的东西。”请注意,当要替换的项目时,此定义不起作用是空列表,要么:
> (tsar '(x () z) '() 'y)
(x () z)
这里有两个问题。当在要转换的列表中遇到列表元素时,代码递归地下降到该列表元素以在其中进行替换;但是当要替换的项目是空列表本身时,列表中没有可替换的内容。这就是(tsar '(x () z) '() y) 不起作用的原因。输入的car 应在 尝试确定是否需要递归下降到一个元素之前与匹配进行检查。这样空列表可以在匹配时立即替换。
第二个问题是 OP 在示例(tsar '(x (x) z) '(x) '(y y)) 中遇到的问题。这里的问题是eq? 测试其参数的身份,但不测试结构相等性,例如,列表相等性。两个列表 '(x) 和 '(x) 只有在它们是相同的对象时才为 eq?,这可能是也可能不是。但它们显然是相同的,因为它们是包含相同元素的列表。要管理它,您应该使用equal?。此处已进行了这两项更改:
(define (tsar subj srch repl)
(if (null? subj)
subj
(if (equal? srch (car subj))
(cons repl (tsar (cdr subj) srch repl))
(if (list? (car subj))
(cons (tsar (car subj) srch repl)
(tsar (cdr subj) srch repl))
(cons (car subj) (tsar (cdr subj) srch repl))))))
这个版本可以按需要工作:
> (tsar '(x (x) z) 'x '(y y))
((y y) ((y y)) z)
> (tsar '(x (x) z) '(x) '(y y))
(x (y y) z)
> (tsar '(x () z) '() 'y)
(x y z)
现在,帖子说必须使用eq? 来解决这个问题。我不确定这是否真的是这样,但eq? 可以用来实现equal? 的一个版本。如果练习的一部分是实现 equal? 的版本,我会敦促 OP 花一些时间尝试解决这个问题不要进一步阅读。
这是完成工作的愚蠢版本:
(define (my-equal? x y)
(if (eq? x y) ; this takes care of atoms
#t
(if (or (or (null? x) (null? y)) ; false if either is '()
(not (and (list? x) (list? y)))) ; false if both are not lists
#f
(if (and (list? (car x)) (list? (car y)))
(and (my-equal? (car x) (car y))
(my-equal? (cdr x) (cdr y)))
(and (eq? (car x) (car y))
(my-equal? (cdr x) (cdr y)))))))
请注意,就eq? 而言,'() 和'() 始终是相同的对象,而eq? 始终返回#t,而symbol=? 将返回#t。 eq? 对数字和字符的行为实际上是实现定义的;在这里使用eqv? 会更好,但eq? 应该可以满足我们的目的。
在上面的定义中,首先用eq?检查输入;如果x 和y 是eq?,那么它们是相同的对象(根据eq?),我们就完成了。
否则,如果x 或y 之一是空列表,我们应该返回#f(因为如果它们两者 '() 那么我们已经返回@987654358 @)。首先针对'() 进行测试的一个原因是,我们可以避免在输入上调用car 或cdr 时出现错误;在 Scheme 中,您不能在空列表中调用这些过程中的任何一个。
或者,如果 两者 x 和 y 都不是列表,我们也应该返回 #f。在这里,我们假设任何不是列表的感兴趣对象都已由eq? 进行了足够彻底的测试。
否则,当两个列表的cars 都是列表时,我们递归地使用my-equal? 来比较这两个元素,并使用and 将比较的结果与比较每个元素的其余部分的结果相结合my-equal? 的输入列表。
否则,输入列表中至少有一个car不是列表,所以我们只使用eq?比较第一个元素,并使用my-equal?比较每个输入的其余部分列表。
有很多方法可以改进上述代码。最好使用cond 而不是if;对于这类问题,通常使用pair? 而不是list? 似乎更好,尽管考虑到代码的结构方式,这并不重要。最好使用equal? 而不是写my-equal?。