0

我正在做 SICP(计算机程序的结构和解释,第 2 版)的练习 2.18 来制作一个反转列表的程序。这是我的代码:

(定义(rev l)
  (如果(空?(cdr l))
    l
    (缺点 (rev (cdr l)) (car l))))

当我用 测试它时(rev (list 1 2 3 4 5)),它返回:

(((((5) . 4) . 3) . 2) . 1)

这对我来说很奇怪。为什么它返回一个级联列表?

当我放:

(缺点 1
      (缺点 2
            (缺点3'())))

它返回(1 2 3)但没有(((1) 2) 3)


我正在使用语言 R5RS 在 DrRacket 中进行 SICP 练习。

我是否犯了任何错误或选择了错误的语言?

4

1 回答 1

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

(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 . ()) . ()),所以你需要做很多练习。

对于在进行 SICP 时使用 DrRacket 中的什么语言,请查看我之前的回答

于 2014-11-25T10:05:42.817 回答