在this页面中,帖子后面有一条评论,其中amb
的执行时间非常短:
(define (amb-backtrack)
(error "no solution found"))
(define (amb . args)
(call/cc (lambda (return)
(let ((backtrack amb-backtrack))
(map (lambda (x)
(call/cc (lambda (k)
(set! amb-backtrack k)
(return x))))
args)
(backtrack 'fail)))))
但我通常会将amb
视为宏来实现 - 在schemers.org常见问题解答中以及Dorai Sitaram's book中:
(define amb-fail '*)
(define initialize-amb-fail
(lambda ()
(set! amb-fail
(lambda ()
(error "amb tree exhausted")))))
(initialize-amb-fail)
(define-macro amb
(lambda alts...
`(let ((+prev-amb-fail amb-fail))
(call/cc
(lambda (+sk)
,@(map (lambda (alt)
`(call/cc
(lambda (+fk)
(set! amb-fail
(lambda ()
(set! amb-fail +prev-amb-fail)
(+fk 'fail)))
(+sk ,alt))))
alts...)
(+prev-amb-fail))))))
所以 - 宏版本更长,更难理解。我看不出它对程序版本的任何好处,当然我宁愿使用程序而不是宏。我错过了什么吗?
答案 0 :(得分:6)
不同之处在于过程调用总是会计算所有参数。
(amb 1 (very-expensive-computation))
将使用amb
的过程版本执行very-expensive-computation
,然后生成1
。如果让1
足以进行所有进一步的计算,那么你就会浪费很多时间来计算其结果值从未使用过的计算。更糟糕的是,正如@Eli Barzilay在评论中提到的那样,amb
被用来模拟无限的非确定性,例如生成所有自然数。
宏版本避免了这种情况,因此它的行为更接近于非确定性编程语言,例如Prolog。