我正在进行SICP(计算机程序的结构和解释,第2版)2.18的练习,以制作一个程序来反转列表。这是我的代码:
(define (rev l) (if (null? (cdr l)) l (cons (rev (cdr l)) (car l))))
当我使用(rev (list 1 2 3 4 5))
对其进行测试时,它返回了:
(((((5) . 4) . 3) . 2) . 1)
这对我很奇怪。为什么它会返回一个级联列表?
我放的时候:
(cons 1 (cons 2 (cons 3 '())))
它返回(1 2 3)
但不是(((1) 2) 3)
。
我正在使用语言R5RS在DrRacket中进行SICP练习。
我是否犯了错误或选择了错误的语言?
答案 0 :(得分:1)
你需要了解一个列表是什么,与它相当亲密,成为一个好的lisper。
列表(1 2 3)
只是(1 . (2 . (3 . ())))
对的视觉糖,可以使用(cons 1 (cons 2 (cons 3 '())))
结构(((((5) . 4) . 3) . 2) . 1)
不是(5)
以外的列表,(5 . ())
是(cons (cons (cons (cons (cons 5 '()) 4) 3) 2) 1)
的视觉糖。可以使用(define (reverse lst)
(define (reverse-aux lst acc)
(if (null? lst)
acc
(reverse-aux (cdr lst) (cons (car lst) acc))))
(reverse-aux lst '()))
制作清单时,您需要从头到尾制作清单。当您遍历列表时,您将从头到尾遍历它。要在处理时以与参数相同的顺序获取列表,您需要使用递归,以便当前步骤等待,直到尾部处理完成,直到最终结果为锥形,或者使用累加器处理列表完成后反转并反转结果。
您似乎正在尝试撤消列表,然后在迭代时只是累积列表:
let
如您所见,我定义了一个辅助函数来获取第三个参数,然后使用它。大多数Schemer更愿意在一个具有命名(define (reverse lst)
(let reverse-aux ((lst lst) (acc '()))
(if (null? lst)
acc
(reverse-aux (cdr lst) (cons (car lst) acc)))))
loop
这些完全一样。通常在进行迭代时,许多人使用名称reverse-aux
代替((a b) c (d))
,但它可以是任何名称,我选择保留第一个示例中的名称。
没有捷径。您需要能够查看(( a . (b ())) . (c . ((d . ()) . ())
并考虑{{1}},因此您需要做很多练习。
在做SICP时,DrRacket使用哪种语言有look at my previous answer。