有人可以帮我解释一下练习3.19的可能解决方案之一。如果列表循环作为参数给出,则程序之谜是无限循环。然而,当我们使用程序eq?检查列表是否包含循环,它可以工作并提供真正的价值。
(define (last-pair x)
(if (null? (cdr x))
x
(last-pair (cdr x))
)
)
(define (make-cycle x)
(set-cdr! (last-pair x) x)
)
(define (mystery x)
(define (loop x y)
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x)
)
)
)
(loop x '())
)
(define t (list 1 2 3))
(define w (make-cycle t))
(eq? (mystery t) t)
它看起来像魔术。我很感激你的帮助。
答案 0 :(得分:4)
mystery
通过反复剪切每个条目的cdr
并将其替换为前一个cdr
的{{1}}来反转“就地”数组。
如果此列表中有 no 循环,那么当您返回原始x
时,它将最终反转。如果是一个循环,你将拥有原始数组的指针。
理解这个问题绝对是个棘手的问题。如果你制作一个盒子和指针图表肯定有帮助,你只需绘制3个图表。
在doing SICP myself的过程中,我发现自己想要一种可视化列表变异的方法(并跳过众多“绘制......”练习的列表图)。我为此写了一个小函数,如果我分享它,我认为你会觉得它很有帮助。
这些图是每次'()
(在x
函数内)运行时loop
运行此函数的示例。
以下代码是我用于生成这些图表的代码。我将此代码编写为Scheme新手,但使用起来非常简单:函数(mystery
)接受参数list->graphviz
,这是您想要的图表列表,也是可选的参数lst
,它为图形提供了一个特殊名称。
graph-name
上面的代码生成Graphviz图形描述语句,然后必须由(define* (list->graphviz lst #:optional graph-name)
"""Convert a list into a set of Graphviz instructions
`lst' is the list you'd like a diagram of
`graph-name` is an optional parameter indicating the name you'd like to give the graph."""
(define number 0)
(define result "")
(define ordinals '())
(define (result-append! str)
(set! result (string-append result str)))
(define* (nodename n #:optional cell)
(format #f "cons~a~a" n (if cell (string-append ":" cell) "")))
(define* (build-connector from to #:optional from-cell)
(format #f "\t~a -> ~a;~%" (nodename from from-cell) (nodename to)))
(define (build-shape elt)
(define (build-label cell)
(cond ((null? cell) "/");; "∅") ; null character
((pair? cell) "*");; "•") ; bullet dot character
(else (format #f "~a" cell))))
(set! number (+ number 1))
(format #f "\t~a [shape=record,label=\"<car> ~a | <cdr> ~a\"];~%"
(nodename number)
(build-label (car elt))
(build-label (cdr elt))))
(define* (search xs #:optional from-id from-cell)
(let ((existing (assq xs ordinals)))
(cond
;; if we're not dealing with a pair, don't bother making a shape
((not (pair? xs)) (result-append! "\tnothing [shape=polygon, label=\"not a pair\"]\n"))
((pair? existing)
(result-append! (build-connector from-id (cdr existing) from-cell)))
(else
(begin
(result-append! (build-shape xs))
(set! ordinals (assq-set! ordinals xs number))
(let ((parent-id number))
;; make a X->Y connector
(if (number? from-id)
(result-append! (build-connector from-id parent-id from-cell)))
;; recurse
(if (pair? (car xs)) (search (car xs) parent-id "car"))
(if (pair? (cdr xs)) (search (cdr xs) parent-id "cdr"))))))))
(search lst)
(string-append "digraph " graph-name " {\n" result "}\n"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;; Here is where `mystery' begins ;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define t '(1 2 3))
(set-cdr! (cddr t) t)
(define (mystery x)
(define (loop x y graph-num)
(display (list->graphviz x (format #f "graph~a" graph-num)))
(if (null? x)
y
(let ((temp (cdr x)))
(set-cdr! x y)
(loop temp x (+ 1 graph-num)))))
(loop x '() 0))
(mystery t)
(Graphviz)处理以呈现为图形格式。
例如,您可以运行上面的代码并将其传递到dot
:
dot
此命令会生成一个postscript文件,如果您多次运行$ scheme generate_box_ptr.scm | dot -o ptrs.ps -Tps
,该文件的优点是可以将每个列表分成自己的页面。 list->graphviz
还可以输出PNG,PDF和许多其他文件格式manpage describes。