`(mcons(#23;()25)16)`和`(mcons 25(mcons 16`()))之间有什么区别?

时间:2015-01-17 22:44:21

标签: scheme racket sicp cons cdr

我忙于Structure and Interpretation of Computer Programs exercise 2.18。在这里,我们必须定义一个反向过程以反转列表。它应该执行以下操作:

(reverse (list 1 4 9 16 25))
;; => (25 16 9 4 1)

我想出了以下定义:

(define (reverse list)
  (if (null? list) 
      list
      (cons (reverse (cdr list)) (car list))))
;; => (mcons (mcons (mcons (mcons (mcons '() 25) 16) 9) 4) 1).

然后在a solution In中找到类似的内容:

(define (reverse items) 
  (if (null? (cdr items)) 
      items 
      (append (reverse (cdr items)) 
              (cons (car items) nil))))
;; => (mcons 25 (mcons 16 (mcons 9 (mcons 4 (mcons 1 '()))))).

appendcons之间有区别,我无法指责。

我的问题:有什么不同,为什么结果不显示为(25 16 9 4 1)

1 个答案:

答案 0 :(得分:5)

简短回答:reverse的第一个版本错误地构建了不正确的列表,第二个版本无法有效地构建正确的列表。只要我们了解appendcons之间的差异,我们就可以做得更好。

append连接两个列表。如果我们使用它只是在一个列表的末尾添加一个元素,我们将完成比需要更多的工作:我们每次必须遍历整个列表 只是放置最后一个元素(参见:Schlemiel the Painter's algorithm)。因此,使用reverse的{​​{1}}实现在复杂性方面可能与append一样糟糕。

另一方面,O(n^2)在列表的头部添加了一个元素,因为cons的实现具有O(n)复杂性。通常,在Scheme中,您应该尽量避免使用reverse来构建新的输出列表,总是更喜欢append。现在,让我们看看您的算法使用cons返回的内容:

cons

为什么?因为要使用(reverse '(1 2 3 4 5)) => '(((((() . 5) . 4) . 3) . 2) . 1) 构建一个正确的列表,第二个参数必须是另一个正确的列表,但是你传递了一个单独的元素。正确的实现需要一个累加器参数 - 顺便说一下,这是更有效的,因为它使用尾递归,这是你应该已经熟悉的概念,正如本书第1.2节中介绍的那样。试试这个:

cons

对于问题的最后部分:列表正在显示(define (reverse lst acc) (if (null? lst) acc (reverse (cdr lst) (cons (car lst) acc)))) (reverse '(1 2 3 4 5) '()) => '(5 4 3 2 1) mcons表示m单元格可变)因为您正在使用的语言,请尝试切换为cons,默认使用不可变 #lang racket单元格,并按预期打印。