;; MATCH.L for PC-LISP.EXE (V2.13) ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;; A DEDUCTIVE DATA BASE RETRIEVER AS PER LISPcraft CHAPTERS 21&22 ;; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ;; This file called match.l implements all of the functions in ;; chapters 21 and 22 of LISPcraft by R.Wilensky. Together they form ;; a deductive data base retriever with two access functions. One is ;; called (insert) and the other (retrieve). Insert takes implications ;; and base cases and inserts them into the given data base. (retrieve) ;; returns a list of matches made with the data base and any bindings ;; neccssary to make the match true. Hence an output like (nil) means ;; one match requiring no bindings. The functions have been slightly ;; modified to run with PC-LISP. Note that they require the PC-LISP.L ;; file to be loaded specificially for the let macro and a few other ;; goodies. If you put PC-LISP.L in the current directory it will be ;; automatically loaded. Or you can put it in a library directory, see ;; the (load) function. ;; ;; August 22nd 1986 ;; Peter Ashwood-Smith ;; ;; Example queries: ;; (mammal Fido) gives (nil) meaning Yes he is a mammal ;; (dog ?x) gives (?x Fido) meaning Yes if (?x is Fido) ;; (mammal ?x) etc.. you get the idea. ;; (? Fido) ;; ;; You really cannot get much out of this example unless you get ;; the LISPcraft book. Have Fun! ;; ;; Main processing Loop - input a data base query, expand the variables ;; ?x to (*var* x) as the read macro in LISPcraft page 295 would do then ;; pass the request to the (retrieve) function. ;; (setsyntax '|?| 'vmacro '(lambda()(list '*var* (read)))) (setq displace-macros t) ;runs much faster if let is displaced at eval time (defun ProcessQueries (data-base) (prog (InputQuery) loop (princ "query?") (setq InputQuery (read)) (cond ((null InputQuery) (return))) (princ "ans=") (patom (CompressVariables (retrieve InputQuery data-base))) (princ (ascii 10)) (go loop) ) ) ;; ;; Opposite of Read Macro for ? - turn list elements like (*var* x) into ;; ?x ;; (defun CompressVariables (List) (cond ((null List) ()) ((atom List) List) ((eq (car List) '*var*) (implode (list '|?| (cadr List))) ) (t (cons(CompressVariables(car List))(CompressVariables (cdr List)))) ) ) ;; ;; top level matcher function, just drives the recursive next level ;; by setting bindings to nil. ;; (defun match (pattern1 pattern2) (match-with-bindings pattern1 pattern2 nil) ) (defun match-with-bindings (pattern1 pattern2 bindings) (cond ((pattern-var-p pattern1) (variable-match pattern1 pattern2 bindings) ) ((pattern-var-p pattern2) (variable-match pattern2 pattern1 bindings) ) ((atom pattern1) (cond ((eq pattern1 pattern2) (list bindings) ) ) ) ((atom pattern2) nil) (t (let ((car-result (match-with-bindings (car pattern1)(car pattern2) bindings))) (and car-result (match-with-bindings (cdr pattern1) (cdr pattern2) (car car-result) ) ) ) ) ) ) (defun variable-match (pattern-var item bindings) (cond ((equal pattern-var item) (list bindings)) (t (let ((var-binding (get-binding pattern-var bindings))) (cond (var-binding (match-with-bindings var-binding item bindings)) ((not (contained-in pattern-var item bindings)) (list (add-binding pattern-var item bindings))) ) ) ) ) ) (defun contained-in (pattern-var item bindings) (cond ((atom item) nil) ((pattern-var-p item) (or (equal pattern-var item) (contained-in pattern-var (get-binding item bindings) bindings) ) ) (t (or (contained-in pattern-var (car item) bindings) (contained-in pattern-var (cdr item) bindings) ) ) ) ) (defun add-binding (pattern-var item bindings) (cons (list pattern-var item) bindings) ) (defun get-binding (pattern-var bindings) (cadr (assoc pattern-var bindings)) ) (defun pattern-var-p (item) (and (listp item) (eq '*var* (car item))) ) ;; ;; Fast Data Base Manager Operations. Using matcher function above to perform ;; deductive retreival. Indexing as per LISPcraft chapter 22. ;; (defun replace-variables(item) (let ((!bindings ())) (replace-variables-with-bindings item))) (defun replace-variables-with-bindings(item) (cond ((atom item) item) ((pattern-var-p item) (let ((var-binding (get-binding item !bindings))) (cond (var-binding) (t (let ((newvar (makevar (gensym 'var)))) (setq !bindings (add-binding item newvar !bindings)) newvar)) ) ) ) (t (cons (replace-variables-with-bindings (car item)) (replace-variables-with-bindings (cdr item)) ) ) ) ) (defun makevar (atom) (list '*var* atom) ) (defun query (request data-base) (apply 'append (mapcar '(lambda(item)(match item request)) data-base ) ) ) (defun index (item data-base) (let ((place (cond ((atom (car item)) (car item)) ((pattern-var-p (car item)) '*var*) (t '*list*) ) ) ) (putprop place (cons (replace-variables item)(get place data-base)) data-base) (putprop data-base (enter place (get data-base '*keys*)) '*keys*) ) ) (defun enter (e l) (cond ((not (memq e l)) (cons e l)) (t l) ) ) (defun fast-query (request data-base) (cond ((pattern-var-p (car request)) (apply 'append (mapcar '(lambda(key)(query request (get key data-base))) (get data-base '*keys*) ) ) ) (t (append (query request (get (cond ((atom (car request)) (car request) ) (t '*list*) ) data-base ) ) (query request (get '*var* data-base)) ) ) ) ) ;; ;; deductive retreiver (LISPcraft page 314) use backward chaining to establish ;; bindings. ;; (defun retrieve (request data-base) (append (fast-query request data-base) (apply 'append (mapcar '(lambda(bindings) (retrieve (substitute-vars (get-binding '(*var* antecedent) bindings) bindings) data-base)) (fast-query (list '<- request '(*var* antecedent)) data-base) ) ) ) ) ;; ;; substitute variables for bindings recursively. LISPcraft page 315. ;; (defun substitute-vars (item bindings) (cond ((atom item) item) ((pattern-var-p item) (let ((binding (get-binding item bindings))) (cond (binding (substitute-vars binding bindings)) (t item) ) ) ) (t (cons (substitute-vars (car item) bindings) (substitute-vars (cdr item) bindings) ) ) ) ) ;; ;; page 315 of LISPcraft add too !d-b1! ;; by calling index to insert the implications and base cases. ;; (index '(<- (scales ?x) (fish ?x)) '!d-b1!) ; fishes have scales (index '(<- (fins ?x) (fish ?x)) '!d-b1!) ; fishes have fins (index '(<- (legs ?x) (mammal ?x)) '!d-b1!) ; some mammals have legs (index '(<- (mammal ?x) (dog ?x)) '!d-b1!) ; a dog is a mammal (index '(<- (dog ?x) (poodle ?x)) '!d-b1!) ; a poodle is a dog (index '(poodle Fido) '!d-b1!) ; fido is a poodle (index '(horse Terry) '!d-b1!) ; terry is a horse (index '(fish Eric) '!d-b1!) ; Eric is a fish ;; ;; start processing queries from data base #1 which was entered above ;; some good things to try are (mammal Fido) which will return (nil) ;; meaning that one match was found needing no bindings to make it true. ;; this was established via the chain (poodle Fido)-->(dog Fido)--> ;; (mammal Fido). ;; (defun run() (ProcessQueries '!d-b1!)) (princ "Data Base Retreiver Loaded and Ready To Go") (princ (ascii 10)) (princ "Just type (run) to start it, have fun.") (princ (ascii 10))