SICP 练习 3.16 为读者提供了一个用于计算列表结构中对数的中断过程:
(define (count-pairs x)
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-pairs (cdr x))
1)))
然后,它要求读者创建由三对组成的列表结构,使此过程返回:
- 3
- 四
- 7
- 永远不要回来。
任务#1和#4很容易;只需制作一个包含三个元素的正常列表并制作一个循环列表即可。但是,对于任务 #2 和 #4,我看到的每个解决方案似乎都通过创建三个以上的对来作弊。例如,方案维基给出了这些:
(define x '(foo))
(define y (cons x x))
(define str2 (list y))
(count-pairs str2) ; 4
(define x '(foo))
(define y (cons x x))
(define str3 (cons y y))
(count-pairs str3) ; 7
(define second (cons 'a 'b))
(define third (cons 'a 'b))
(define first (cons second third))
(set-car! third second)
(count-pairs first) ;; => 4
(define third (cons 'a 'b))
(define second (cons third third))
(define first (cons second second))
(count-pairs first) ;; => 7
出于同样的原因,我不同意所有这些:它们似乎不止三对。例如,第一个代码块的最终列表将(cons (cons (cons 'foo nil) (cons 'foo nil)) nil)
。正如我写过三次以上cons
的那样,这不是由三对组成的。同样,最后一个区块显然是(cons (cons (cons 'a 'b) (cons 'a 'b)) (cons (cons 'a 'b) (cons 'a 'b)))
的,远远超过三对。
我的误会在哪里?
写成
(define x (cons 'foo '()))
(define y (cons x x))
(define str2 (cons y '()))
多少cons
?三cons
!但是当我们数它们时,x
被数了两次,所以(cons x x) ==> (+ 1 1 1) = 3
然后(cons y '()) => (+ 3 1) = 4
.但那里仍然有三cons
细胞新鲜铸造。
写成
(define x (cons 'foo '()))
(define y (cons x x))
(define str3 (cons y y))
多少cons
?三cons
!但是当我们数它们时,x
和y
被数了两次,所以(cons x x) ==> (+ 1 1 1) = 3
然后(cons y y) => (+ 3 3 1) = 7
.但那里仍然只有三个cons
细胞新鲜铸造。
这两个示例利用列表结构共享。y
持有的 cons 单元格的car
和cdr
字段都指向由x
持有的同一 cons 单元格。
但该程序不知道这一点。它进入同一个缺点单元格 -x
- 两次,并计数两次。
它可以测试相同性,eq?
.(eq? (car y) (cdr y))
会返回#t
.但它不会发出这个电话。
由str2
和str3
持有的两个列表结构可以表示为 -- 第一个,第一个,作为 --
str2 --> [ y | [] ] ; first CONS cell
|
|
[ x | x ] ; second CONS cell
/
/
[ FOO | [] ] ; third CONS cell
当你写(cons (cons (cons 'foo nil) (cons 'foo nil)) nil)
时,你是在按值写出同样的东西,但不是在结构上。
价值相等是用equal?
来检查的——比如,"打印出来的两件事是一样的吗?当通过纯粹的手段观察时,它们是无法区分的吗?..."(这里纯洁的意思是,不雇用eq?
)。
结构相等性是用eq?
来检查的——比如,">计算机内存中的两件事实际上是一回事吗?...">
纯"函数式编程关注抽象的"值"。
不纯编程关注计算机内存中的具体结构。
<小时 />另一个值是
str3 --> [ y | y ]
/
/
[ x | x ]
/
/
[ FOO | [] ]