`amb`运算符作为宏或过程?

Jay*_*Jay 5 macros scheme non-deterministic

这个页面中,在帖子之后有一个评论,它给出了一个非常短的实现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)))))
Run Code Online (Sandbox Code Playgroud)

但我通常看到amb实现为宏 - 在schemers.org常见问题解答中,以及在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))))))
Run Code Online (Sandbox Code Playgroud)

所以 - 宏版本更长,更难理解.我看不出它对程序版本的任何好处,当然我宁愿使用程序而不是宏.我错过了什么吗?

Fre*_*Foo 6

不同之处在于过程调用始终会评估所有参数.

(amb 1 (very-expensive-computation))
Run Code Online (Sandbox Code Playgroud)

将使用程序版本amb执行very-expensive-computation,然后执行1.如果1让所有进一步计算都足够,那么你在计算中浪费了大量时间,其结果值从未使用过.更糟糕的是,正如@Eli Barzilay在评论中提到的那样,当amb用于模拟无限的非确定性时,例如生成所有自然数.

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

  • 它不仅仅是浪费时间 - 考虑一个带有第二个'amb`子表单的函数,它在无限循环中调用函数.(这甚至不是一些没有用的学术性事物:对于所有整数实现"amb"将是一种自然的方式.)如果`amb`是一个简单的函数,这样的事情是不可能的. (3认同)