6

页面中,帖子后面有一条评论,它给出了一个非常简短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 FAQ 和Dorai Sitaram 的书中

(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))))))

所以——宏版本更长,更难理解。我看不出它比过程版本有什么优势,当然我宁愿使用过程而不是宏。有什么我错过的吗?

4

1 回答 1

7

不同之处在于过程调用总是评估所有参数。

(amb 1 (very-expensive-computation))

将使用 的过程版本amb执行very-expensive-computation,然后屈服1。如果 yield1足以满足所有进一步的计算,那么您已经在从未使用过结果值的计算上浪费了大量时间。正如@Eli Barzilay 在评论中提到的那样,更糟糕的事情发生了,whenamb被用来模拟无限的非确定性,例如生成所有自然数。

宏版本避免了这种情况,因此它的行为更接近于非确定性编程语言,如 Prolog。

于 2011-04-24T14:59:57.123 回答