我修改了SICP中count-change
函数的代码,以便在函数递归时显示一对。该对的格式为"(cc a k)" -> "(cc a (- k 1))"
或"(cc a k)" -> "(cc (- a (d k)) k)"
,我的目标是使用GraphViz构建一个DOT文件以显示树递归。
这是一个示例图片,由以下代码生成:
这是方案代码:
; Count Change
(define (count-change amount)
(cc amount 5))
(define (cc amount kinds-of-coins)
(begin
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else (+
(begin
(display "\"")
(display `(cc ,amount ,kinds-of-coins))
(display "\"")
(display " -> ")
(display "\"")
(display `(cc ,amount ,(- kinds-of-coins 1)))
(display "\"")
(display "\n")
(cc amount (- kinds-of-coins 1))
)
(begin
(display "\"")
(display `(cc ,amount ,kinds-of-coins))
(display "\"")
(display " -> ")
(display "\"")
(display `(cc ,(- amount (first-denomination kinds-of-coins)) ,kinds-of-coins))
(display "\"")
(display "\n")
(cc (- amount (first-denomination kinds-of-coins)) kinds-of-coins)
)
)
))))
; first-denomination takes the number of kinds of coins and returns the denomination of the first kind
(define (first-denomination kinds-of-coins)
(cond ((= kinds-of-coins 1) 1)
((= kinds-of-coins 2) 5)
((= kinds-of-coins 3) 10)
((= kinds-of-coins 4) 25)
((= kinds-of-coins 5) 50)))
(count-change 11)
原始代码为here。
我已经阅读过有关方案宏的内容,我认为他们可以通过允许我在带有显示的begin
语句中“封装”对(cc。)的调用来解决这个问题,以输出正在发生的事情。递归时间。
如何使用Scheme宏完成此操作?
注意:我知道我的图像不准确,我需要找到一种使节点不同的方法,这样图形就是树,而不仅仅是DAG。但是,这超出了这个问题的范围。
答案 0 :(得分:4)
宏不是你想要的。这个工作更合适的工具是一个简单的本地函数,它知道cc
的旧参数和新参数,并处理打印graphviz文本并进行递归调用:
(define (cc amount kinds-of-coins)
(let ((recur (lambda (new-amount new-kinds)
(begin
(display "\"")
(display `(cc ,amount ,kinds-of-coins))
(display "\"")
(display " -> ")
(display "\"")
(display `(cc ,new-amount ,new-kinds))
(display "\"")
(display "\n")
(cc new-amount new-kinds)))))
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else (+
(recur amount (- kinds-of-coins 1))
(recur (- amount (first-denomination kinds-of-coins)) kinds-of-coins))))))
您没有说明您正在使用哪种Scheme实现,但是对于某些实现,还可以执行一些小的语法清理,以使此代码看起来更好。
答案 1 :(得分:2)
这是一种抽象jacobm建议的模式的方法:
;; Add graphviz tracing to a definition:
(define-syntax define/graphviz-trace
(syntax-rules ()
[(_ (id args ...) body ...)
(define (id args ...)
(let* ([real-id id]
[old-args (list args ...)]
[id (lambda (args ...)
(define new-args (list args ...))
(print-trace 'id old-args new-args)
(real-id args ...))])
body ...))]))
;; print-trace: symbol list list -> void
(define (print-trace id old-args new-args)
(display "\"")
(display `(id ,@old-args))
(display "\"")
(display " -> ")
(display "\"")
(display `(id ,@new-args))
(display "\"")
(display "\n"))
; first-denomination takes the number of kinds of coins and
; returns the denomination of the first kind
(define (first-denomination kinds-of-coins)
(cond ((= kinds-of-coins 1) 1)
((= kinds-of-coins 2) 5)
((= kinds-of-coins 3) 10)
((= kinds-of-coins 4) 25)
((= kinds-of-coins 5) 50)))
;; Example:
(define/graphviz-trace (cc amount kinds-of-coins)
(cond ((= amount 0) 1)
((or (< amount 0) (= kinds-of-coins 0)) 0)
(else (+ (cc amount (- kinds-of-coins 1))
(cc (- amount (first-denomination kinds-of-coins))
kinds-of-coins)))))