3.12 Prolog
You must complete this assignment using a specific language. Choose Determine language from source in the DrRacket menu, then writeas the first line of your program, overwritting the default #lang at the top of the file.
In this assignment, you will implement Prolog-style logic variables and backtracking search using Racket’s macros and continuations.
In this assignment, a prolog-expression? is a function that accepts a failure-continuation? (which it calls when it fails) and returns a success-continuation? (each time it succeeds).
A failure-continuation? is a function that accepts a value (which it ignores) and fails; failure may mean aborting the entire Prolog search (meaning it returns nothing), or it may mean backtracking to an earlier point and trying something different (meaning it returns a success-continuation?).
A success-continuation? is a function that accepts a value (which it ignores) and tries again; trying again may not be possible, so the search is aborted (meaning it returns nothing), or it may mean backtracking to an earlier point and trying something different (meaning it returns a success-continuation?).
You must define a new data structure for logic-variable?s. The assignment text imposes some constraints on this data structure. This data structure will contain prolog-value?s. prolog-value?s are booleans, numbers, strings, symbols, logic-variable?s, empty, or cons cells that only contain prolog-value?s.
You must implement the following, and I suggest writing them in this order:
value
value
procedure
(=and2 e1 e2) → prolog-expression?
e1 : prolog-expression? e2 : prolog-expression?
For example, (=and2 (=or2 =succeed =succeed) =succeed) succeeds 2 times.
procedure
(=or2 e1 e2) → prolog-expression?
e1 : prolog-expression? e2 : prolog-expression?
For example, (=or2 (=or2 =succeed =succeed) =succeed) succeeds 3 times.
syntax
(=and e ...)
e : prolog-expression?
syntax
(=or e ...)
e : prolog-expression?
syntax
(=find-some n (v ...) e)
n : exact-nonnegative-integer?
v : identifier?
e : prolog-expression?
The solutions should be returned in the order of their discovery (by a left-to-right depth-first search) and each solution should list the variables in the order provided. The search is bound to at most n solutions. This makes it possible to test queries with infinite solutions.
(=find-some 3 (x y) (=or (=and (== x 1) (== y 2)) (=and (== x 3) (== y 4)) (=and (== x 5) (== y 6))))
In your first version of this, before you’ve implemented =var, just ignore the new logic variables.
This macro should probably be paired with a function that does most of the work.
syntax
(=find-all (v ...) e)
v : identifier?
e : prolog-expression?
procedure
(_) → logic-variable?
syntax
(=var (v ...) e)
v : identifier?
This can be implemented by expanding to let.
procedure
(== e1 e2) → prolog-expression?
e1 : prolog-value? e2 : prolog-value?
If e1 and e2 can be unified, the expression succeeds. If they cannot be unified, the expression fails. Unification must always terminate: you must implement an occurs check.
3.12.1 Interactive Testing
(define resumer-box (box 'resumer)) (define (next) ((unbox resumer-box) 'dummy)) (define-syntax show (syntax-rules () [(show (vars ...) e) (=var (vars ...) (let/cc esc (let/cc fk (esc (let ([next (e fk)]) (set-box! resumer-box next) (printf "~a: ~a~n" 'vars (lv-value vars)) ... (void)))) 'fail))]))
where lv-value accesses the value stored in the given logic variable.
Please note that these definitions are very implementation specific.
3.12.2 Testing Harness
; symbol -> boolean : returns true if and only if the given ; symbol is an uninterned symbol (define (gensym? s) (not (equal? (string->symbol (symbol->string s)) s))) ; list X list -> boolean : takes two bindings (for example, ; returned from =find-all) and returns true if and only if ; the two variables they bind are equal to the same thing. (define (binding-equal? b1 b2) (equal? (second b1) (second b2))) ; Prolog-Expr X symbol X bool-Exprs ... -> Test result : Takes a ; (=find-all ...) or (=find-some ...), an identifier, and a set of ; expressions that evaluate to booleans when the identifier is ; bound to the result of the given Prolog-expr. The test passes ; if and only if all of the boolean expressions are true. (define-syntax test-prolog (syntax-rules (test-prolog) [(test-prolog find-expr result-id bool ...) (test (let ([result-id find-expr]) (and bool ...)) true)]))
3.12.3 Additional Notes
Do not use global variables for this assignment. We expect your implementation to have the ability to do a query, capture the continuation returned, do a different query, and then invoke the first continuation with no trouble.
Unbound variables should have a unique “value” in their output. That is, if y is unbound, it should have a unique symbol as its value. If x and y are dependently unbound, they should have the same unique symbol as their values. For example, in the evaluation of (=var (x y) (== x y)), x and y are dependently unbound.
Remember that order counts. Please carefully read the definitions for =find-all and =find-some. Even if your results are correct, if they are in the wrong order or format, they will be marked as incorrect.
3.12.4 Example 1: Simple Search
(test-prolog (=find-all () (=or (=and =succeed =succeed) (=and =fail =succeed =succeed) (=and =succeed))) result (= (length result) 2))
3.12.5 Example 2: Family Trees
(define (=parent c p) (=or (=and (== c 'vito) (== p 'dom)) (=and (== c 'sonny) (== p 'vito)) (=and (== c 'michael) (== p 'vito)) (=and (== c 'fredo) (== p 'vito)) (=and (== c 'sophia) (== p 'michael)) (=and (== c 'tony) (== p 'michael)))) (define (=ancestor X Y) (=or (=parent X Y) (=var (Z) (=and (=parent X Z) (=ancestor Z Y)))))
> (show (x) (=ancestor x 'vito))
x: sonny |
> (next)
x: michael |
> (next)
x: fredo |
> (next)
x: sophia |
> (next)
x: tony |
> (next)
> (find-all (x) (=parent x 'michael))
> (find-some 3 (x y) (=ancestor x y))