我们可以为 amb 设计一些辅助函数,使用它们我们可以清晰的表达 经常用到的信息。由于我的代码里多次使用这些函数,以后我们用到 这些函数时就不再列出代码。
(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) 就可以省去你打很多数字了 :)
(define assert (lambda (pred) (if (not pred) (amb))))
我们可以用 assert 来插入一个断言。这样可以使程序的表达更加清晰明确。
(define-syntax apply-amb (syntax-rules () ((apply-amb ls) (eval `(amb ,@ls) (interaction-environment)))))
当我们需要把 amb 作用于一个从别处返回的列表时,可以用这个宏。
(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,它返回一个“有意义的结果”。
用来判断一个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))))))
用来从一个 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))))))))