第三章

一些方便的辅助函数

我们可以为 amb 设计一些辅助函数,使用它们我们可以清晰的表达 经常用到的信息。由于我的代码里多次使用这些函数,以后我们用到 这些函数时就不再列出代码。

3.1  number-between

(define number-between
  (lambda (lo hi)
    (let loop ((i lo))
      (if (> i hi) (amb)
          (amb i (loop (+ i 1)))))))

这个函数是用来方便的构造一个 amb 数字选择的。比如

(number-between 1 8)

就相当于

(amb 1 2 3 4 5 6 7 8)

如果是 (number-between 1 100) 就可以省去你打很多数字了 :)

3.2  assert

(define assert
  (lambda (pred)
    (if (not pred) (amb))))

我们可以用 assert 来插入一个断言。这样可以使程序的表达更加清晰明确。

3.3  apply-amb

(define-syntax apply-amb
  (syntax-rules ()
    ((apply-amb ls)
     (eval `(amb ,@ls) (interaction-environment)))))

当我们需要把 amb 作用于一个从别处返回的列表时,可以用这个宏。

3.4  bag-of

(define-syntax bag-of
  (syntax-rules ()
    ((bag-of e)
     (let ((prev-amb-fail amb-fail)
           (results '()))
       (if (call/cc
            (lambda (k)                                                
              (set! amb-fail (lambda () (k #f)))                ;<-----+
              (let ((v e))             ;amb-fail will be modified by e |
                (set! results (cons v results))                       ;|
                (k #t))))                                             ;|
           (amb-fail))                 ;so this amb-fail may not be ---+
       (set! amb-fail prev-amb-fail)
       (reverse! results)))))

amb 每次只返回一个结果。所以如果想得到所有可以使得程序“不失 败”的结果。你需要多次调用 (amb)。为了一次性得到所有结果,你 可以用 bag-of.

bag-of 接受一个参数,这是一个表达式,这个表达式里面可以调用 amb,它返回一个“有意义的结果”。

3.5  distinct?

用来判断一个list里的元素是不是没有重复。

(define (distinct? . ls)
  (let loop ((lst (car ls)))
    (let ((first (car lst)) (rest (cdr lst)))
      (cond 
       ((null? rest) #t)
       ((member first rest) #f)
       (else (loop rest))))))

3.6  del

用来从一个 list 里删除一个元素。但是它先把原来的 list 复制一份,并不修改原来的 list.

(define (del n ls)
  (let ((ls (reverse (reverse ls))))
    (cond ((null? ls) ls)
          ((eqv? n (car ls)) (cdr ls))
          (else 
           (let loop ((l (cdr ls)) (last ls))
             (cond ((null? l) ls)
                   ((equal? n (car l))
                    (set-cdr! last (cdr l))
                    ls)
                   (else (loop (cdr l) l))))))))