home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / files / language / examples / prolog.lsp < prev    next >
Encoding:
Lisp/Scheme  |  1993-10-23  |  16.0 KB  |  424 lines

  1. ; Prolog-Interpreter: eXPerimental-Prolog
  2. ; Bruno Haible 21.04.1988-22.04.1988
  3.  
  4. (setq *print-pretty* t)
  5. (setq *print-circle* t)
  6. (defvar prologpackage
  7.   (or (find-package "_") (make-package "_" :nicknames '("PROLOGVARS")))
  8. )
  9. (defun c ()
  10.   (compile-file "PROLOG.LSP" :output-file "PROLOG.FAS")
  11.   (load "PROLOG.FAS")
  12. )
  13.  
  14. #| Unifikation: nach Robinson, mit Zyklen |#
  15.  
  16. ; Variablen sind Symbole.
  17. ; Sie werden dadurch an etwas gebunden, daß ihre Wertzelle diesen Wert
  18. ; enthält.
  19.  
  20. ; Generierung einer Variablen mit vorgegebenem Namensanfang:
  21. (defun make-var (name)
  22.   (let ((var (gentemp (string name) prologpackage)))
  23.     (set var var)
  24.     var
  25. ) )
  26.  
  27. ; Test auf Variable
  28. (defun varp (term)
  29.   (and (symbolp term) (eq (symbol-package term) prologpackage))
  30. )
  31.  
  32. ; Test auf gebundene Variable
  33. (defun boundvarp (term)
  34.   (and (varp term) (not (eq (symbol-value term) term)))
  35. )
  36.  
  37. ; Generierung einer Bindung
  38. (defun bind (var term)
  39.   (set var term)
  40. )
  41.  
  42. ; Substitution = Menge aller Paare (X . W), wo X eine gebundene Variable
  43. ; und W der Inhalt ihrer Wertzelle ist.
  44.  
  45. (defun check-circularity (var)
  46.   ; liefert T, falls var (evtl. über andere Variablen hinweg)
  47.   ; an sich selbst gebunden ist.
  48.   (let ((step1 var)  ; Vorgehen wie bei list-length
  49.         (step2 var))
  50.     (loop
  51.       (if (eq (symbol-value step2) step2) ; Variable ungebunden?
  52.         (return T))
  53.       (setq step2 (symbol-value step2)) ; eins weiterrücken
  54.       (if (not (varp step2)) ; Ende der Liste ?
  55.         (return NIL))
  56.       (if (eq step1 step2) ; in Zyklus gelaufen?
  57.         (return T))
  58.       (if (eq (symbol-value step2) step2) ; Variable ungebunden?
  59.         (return T))
  60.       (setq step2 (symbol-value step2)) ; wieder eins weiterrücken
  61.       (if (not (varp step2)) ; Ende der Liste ?
  62.         (return NIL))
  63.       (if (eq step1 step2) ; in Zyklus gelaufen?
  64.         (return T))
  65.       (setq step1 (symbol-value step1))
  66.       ; step1 folgt mit halber Geschwindigkeit nach.
  67. ) ) )
  68.              
  69. (defun make-lispterm (term)
  70.   ; wandelt einen Prolog-Term durch Ausschaltung von Zwischenvariablen
  71.   ; zu einem reinen Lisp-Term um und liefert diesen.
  72.   (let ((markedvars nil))
  73.     (labels ((unmarked (var)
  74.               (eq (get var 'newreference '%default%) '%default%)
  75.              )
  76.              (mark (var)
  77.                ; markiert die Variable var, indem es in die Propertyliste
  78.                ; von var das einträgt, worauf jede Referenz
  79.                ; auf var zu zeigen haben wird.
  80.                (when (unmarked var) ; schon markiert -> nichts tun.
  81.                  (push var markedvars)
  82.                  (let ((val (symbol-value var)))
  83.                    (setf (get var 'newreference)
  84.                      (cond ((varp val) (mark val)) ; bei Variablen: rekursiv
  85.                            ((consp val) ; neue CONS-Zelle anlegen:
  86.                             (cons (car val) (cdr val)) )
  87.                            (t val) ; Atome: Wert selbst
  88.                ) ) ) )
  89.                (get var 'newreference)
  90.              )
  91.              (lispterm (term) ; der eigentliche Umwandler
  92.                (cond ((varp term)
  93.                       (if (check-circularity term)
  94.                         (error "Ungebundene Variable ~S kann nicht in ~
  95.                            LISP-Objekt umgewandelt werden." term
  96.                       ) )
  97.                       (if (unmarked term)
  98.                         (let ((newval (mark term)))
  99.                           (when (consp newval)
  100.                             (setf (car newval) (lispterm (car newval)))
  101.                             (setf (cdr newval) (lispterm (cdr newval)))
  102.                           )
  103.                           newval
  104.                         )
  105.                         (get term 'newreference)
  106.                      ))
  107.                      ((consp term)
  108.                       (cons (lispterm (car term)) (lispterm (cdr term)))
  109.                      )
  110.                      (t term)
  111.             )) )
  112.       (prog1
  113.         (lispterm term)
  114.         (dolist (v markedvars) ; alle Markierungen rückgängig machen
  115.           (remprop v 'newreference)
  116.       ) )
  117. ) ) )
  118.  
  119. ; Damit die PRINT-CIRCLE-Markierungen in den verschiedenen Teilen der
  120. ; Ausgabe dieselben Nummern tragen, muß man die Teile per DEFSTRUCT
  121. ; zusammenfassen und mit einer eigenen PRINT-FUNCTION ausgeben:
  122. (defstruct (vars-and-vals (:print-function print-vars-and-vals) (:conc-name "VV-"))
  123.   (vars nil :type list)
  124.   (vals nil :type list)
  125. )
  126. (defun print-vars-and-vals (obj stream depth)
  127.   (declare (ignore depth))
  128.   (format stream "~{~W = ~W~^, ~}" (mapcan #'list (vv-vars obj) (vv-vals obj)))
  129. )
  130.  
  131. (defun print-bindings (varnames newvars)
  132.   ; gibt die Werte der Variablen in newvars aus, dabei dienen die Symbole
  133.   ; in varnames als Namen.
  134.   (let ((markedvars nil))
  135.     (labels ((unmarked (var)
  136.               (eq (get var 'newreference '%default%) '%default%)
  137.              )
  138.              (mark (var)
  139.                ; markiert die Variable var, indem es in die Propertyliste
  140.                ; von var dasjenige Objekt einträgt, worauf jede Referenz
  141.                ; auf var zu zeigen haben wird. Liefert dieses Objekt.
  142.                (when (unmarked var) ; schon markiert -> nichts tun.
  143.                  (push var markedvars)
  144.                  (let ((val (symbol-value var)))
  145.                    (setf (get var 'newreference)
  146.                      (cond ((varp val) (mark val)) ; bei Variablen: rekursiv
  147.                            ((consp val) ; neue CONS-Zelle anlegen:
  148.                             (cons (car val) (cdr val)) )
  149.                            (t val) ; Atome: Wert selbst
  150.                ) ) ) )
  151.                (get var 'newreference)
  152.              )
  153.              (markcircular (var)
  154.                ; markiert var (noch nicht markiert) und alle mit var
  155.                ; geshareten Variablen mit demselben Symbol,
  156.                ; liefert dieses Symbol.
  157.                ; 1. Schritt: Suchen, ob eine der Variablen bereits markiert.
  158.                (let ((markingvar
  159.                        (do ((step1 var) (step2 var))
  160.                            (nil)
  161.                          (setq step2 (symbol-value step2))
  162.                          (unless (unmarked step2)
  163.                            (return (get step2 'newreference)))
  164.                          (if (eq step1 step2) (return var))
  165.                          (setq step2 (symbol-value step2))
  166.                          (unless (unmarked step2)
  167.                            (return (get step2 'newreference)))
  168.                          (if (eq step1 step2) (return var))
  169.                          (setq step1 (symbol-value step1))
  170.                     )) )
  171.                  ; 2. Schritt: diese Markierung überall unterbringen.
  172.                  (do ((step1 var) (step2 var))
  173.                      (nil)
  174.                    (pushnew step2 markedvars)
  175.                    (setf (get step2 'newreference) markingvar)
  176.                    (setq step2 (symbol-value step2))
  177.                    (if (eq step1 step2) (return))
  178.                    (pushnew step2 markedvars)
  179.                    (setf (get step2 'newreference) markingvar)
  180.                    (setq step2 (symbol-value step2))
  181.                    (if (eq step1 step2) (return))
  182.                    (setq step1 (symbol-value step1))
  183.                  )
  184.                  markingvar
  185.              ) )
  186.              (lispterm (term) ; der eigentliche Umwandler
  187.                (cond ((varp term)
  188.                       (if (check-circularity term)
  189.                         (if (unmarked term)
  190.                           (markcircular term)
  191.                           (get term 'newreference)
  192.                         )
  193.                         (if (unmarked term)
  194.                           (let ((newval (mark term)))
  195.                             (when (consp newval)
  196.                               (setf (car newval) (lispterm (car newval)))
  197.                               (setf (cdr newval) (lispterm (cdr newval)))
  198.                             )
  199.                             newval
  200.                           )
  201.                           (get term 'newreference)
  202.                      )) )
  203.                      ((consp term)
  204.                       (cons (lispterm (car term)) (lispterm (cdr term)))
  205.                      )
  206.                      (t term)
  207.             )) )
  208.       (let ((values (mapcar #'lispterm newvars)))
  209.         (print (make-vars-and-vals :vars varnames :vals values))
  210.         (dolist (v markedvars) ; alle Markierungen rückgängig machen
  211.           (remprop v 'newreference)
  212.       ) )
  213. ) ) )
  214.  
  215.  
  216. ; unifiziert mehrere Terme, erweitert dabei die (globale) Substitution:
  217. ; Hier: Unifikation nach Robinson.
  218. ; Ergebnis: 'IMPOSSIBLE oder
  219. ;           eine Liste ((X1 . T1) ... (Xj . Tj)) der alten Werte der bei der
  220. ;           Unifikation veränderten Variablen.
  221. (defun unify (termpairlist &aux (oldvaluelist nil))
  222.   ; termpairlist = ((T1a T1b) ... (Tka Tkb)), wobei Tia mit Tib zu unifizieren
  223.   ; ist.
  224.   (loop
  225.     (if (null termpairlist) (return oldvaluelist))
  226.     (let ((Ta (first (first termpairlist)))
  227.           (Tb (second (first termpairlist))))
  228.       (setq termpairlist (cdr termpairlist))
  229.       (cond ((eq Ta Tb)) ; gleich -> OK
  230.             ((varp Ta)
  231.              (let ((oldvalue (symbol-value Ta)))
  232.                (push (cons Ta oldvalue) oldvaluelist)
  233.                (bind Ta Tb)
  234.                (push (list oldvalue Tb) termpairlist)
  235.             ))
  236.             ((varp Tb)
  237.              (let ((oldvalue (symbol-value Tb)))
  238.                (push (cons Tb oldvalue) oldvaluelist)
  239.                (bind Tb Ta)
  240.                (push (list Ta oldvalue) termpairlist)
  241.             ))
  242.             ((and (consp Ta) (consp Tb))
  243.              (push (list (cdr Ta) (cdr Tb)) termpairlist)
  244.              (push (list (car Ta) (car Tb)) termpairlist)
  245.             )
  246.             (t
  247.              (undobinds oldvaluelist)
  248.              (return 'impossible)
  249.             )
  250. ) ) ) )
  251.  
  252. (defun undobinds (oldvaluelist)
  253.   (dolist (pair oldvaluelist) (set (car pair) (cdr pair)))
  254. )
  255.  
  256. #| Kontrollstruktur: Box
  257.                 +----------+
  258.    ----CALL---->|          |-----RETURN---->
  259.                 |          |
  260.    <---FAIL-----|          |<----REDO-------
  261.                 +----------+
  262.  
  263.    Bei CALL und RETURN wächst der Stack, bei FAIL und REDO schrumpft er.
  264.    CALL ist ein normaler Prozedur-Aufruf,
  265.    FAIL ist ein normaler Prozedur-Rücksprung, der beim Aufrufer als REDO wirkt.
  266.    RETURN ist (da der Stack wachsen muß) auch ein Aufruf!
  267. |#
  268.  
  269. (defstruct (hornclause :constructor
  270.   (:constructor make-clause (vars head &optional body)))
  271.   vars ; Liste von Variablen, die in head und body zu instantiieren sind
  272.   head ; Der Kopf der Klause, mit ihr ist zu unifizieren
  273.   body ; Die zu erfüllende Form, meist (and form1 ... formk)
  274.        ; nil [für Klausen 'fact.'] bedeutet soviel wie (and) : sofort erfüllt.
  275. )
  276.  
  277. ; Prädikate, die Lisp-Funktionen sind, werden als Primitives angesehen.
  278. (defun primitivep (pred)
  279.   (and (null (get pred 'prolog)) (fboundp pred))
  280. )
  281.  
  282. ; versucht alle Lösungen des Goals form zu bestimmen, liefert die
  283. ; Lösungen einzeln mit Aufruf von returnfun, und führt bei einem Fail
  284. ; einen normalen Rücksprung aus.
  285. (defun prolog-form (form returnfun &optional cut-tag)
  286.   (let ((pred (first form)))
  287.     (cond ((eq pred 'fail)) ; sofort return-> fail, Ignorierung von returnfun
  288.           ((eq pred 'is)
  289.            (unless (varp (second form))
  290.              (error "Zuweisung an ~S mit IS unmöglich, da keine Variable")
  291.            )
  292.            (let* ((var (second form))
  293.                   (oldvaluelist
  294.                     (unify (list (list
  295.                                    var
  296.                                    (eval (make-lispterm (third form)))
  297.                  )) )      )     )
  298.              (if (listp oldvaluelist)
  299.                (unwind-protect
  300.                  (funcall returnfun)
  301.                  (undobinds oldvaluelist)
  302.           )) ) )
  303.           ((eq pred 'and)
  304.            (funcall
  305.              (reduce
  306.                #'(lambda (formi fun)
  307.                    (function (lambda () (prolog-form formi fun cut-tag)))
  308.                  )
  309.                (cdr form)
  310.                :from-end t :initial-value returnfun
  311.           )) )
  312.           ((eq pred 'or)
  313.            (dolist (subform (cdr form))
  314.              (prolog-form subform returnfun cut-tag)
  315.              ; nach fail die nächste Subform probieren
  316.           ))
  317.           ((eq pred '!) ;cut
  318.            (funcall returnfun) ; eine Lösung suchen
  319.            (throw cut-tag nil) ; danach das ganze Prädikat abbrechen
  320.              ; (Bindungen rückgängig machen nicht vergessen!)
  321.           )
  322.           ((eq pred 'call)
  323.            (prolog-form (make-lispterm (second form)) returnfun cut-tag)
  324.           )
  325.           ((get pred 'prolog)
  326.            (if (get pred 'prologtraced)
  327.              (print-bindings '(call) (list form))
  328.            )
  329.            (let* ((new-cut-tag (gensym))
  330.                   (clauses (get pred 'prolog)))
  331.              (catch new-cut-tag
  332.                (dolist (clause clauses)
  333.                  (let* ((oldvars (hornclause-vars clause))
  334.                         (head (hornclause-head clause))
  335.                         (body (hornclause-body clause)))
  336.                    (if (/= (length form) (length head))
  337.                      (error "Bad number of arguments to ~S, wanted ~S, ~
  338.                              received ~S" pred (length head) (length form)
  339.                    ) )
  340.                    (dolist (var oldvars)
  341.                      (let ((newvar (make-var var)))
  342.                        (setq head (subst newvar var head))
  343.                        (setq body (subst newvar var body))
  344.                    ) )
  345.                    (let* ((returnfun1
  346.                             (if (get pred 'prologtraced)
  347.                               (function
  348.                                 (lambda ()
  349.                                   (print-bindings '(return) (list form))
  350.                                   (funcall returnfun)
  351.                                   (print-bindings '(redo) (list form))
  352.                               ) )
  353.                               returnfun
  354.                           ) )
  355.                           (oldvaluelist (unify (list (list form head)))))
  356.                      (if (listp oldvaluelist)
  357.                        (unwind-protect
  358.                          (if body
  359.                            (prolog-form body returnfun1 new-cut-tag)
  360.                            (funcall returnfun1)
  361.                          )
  362.                          (undobinds oldvaluelist)
  363.                    ) ) ) ; bei IMPOSSIBLE sofort FAIL für diese Clause.
  364.            ) ) ) )
  365.            (if (get pred 'prologtraced)
  366.              (print-bindings '(fail) (list form))
  367.           ))
  368.           ((primitivep pred)
  369.            (apply pred (mapcar #'make-lispterm (cdr form)))
  370.            (funcall returnfun) ; ein REDO ergibt automatisch ein FAIL.
  371.           )
  372.           (t (cerror "It will fail." "Undefined predicate ~S" pred))
  373. ) ) )
  374.  
  375.  
  376. ; Definition eines Prolog-Prädikates
  377. (defmacro deflog (name &rest clauses)
  378.   `(setf (get ',name 'prolog)
  379.          ',(mapcar #'(lambda (clause) (apply #'make-clause clause))
  380.                    clauses
  381.    )       )
  382. )
  383.  
  384. (defmacro prologtrace (name)
  385.   `(setf (get ',name 'prologtraced) T)
  386. )
  387.  
  388. (defmacro prologuntrace (name)
  389.   `(remprop ',name 'prologtraced)
  390. )
  391.  
  392.  
  393. (defun goal1 (vars form &aux (newvars nil)) ; löst form nach vars auf.
  394.   (if (null vars)
  395.     (block maingoal
  396.       (catch 'main-cut-tag
  397.         (prolog-form form
  398.           #'(lambda () (format t "~%Yes.") (return-from maingoal nil))
  399.           'main-cut-tag
  400.         )
  401.         (format t "~%No.")
  402.     ) )
  403.     (prog ((foundsome nil))
  404.       (dolist (var vars)
  405.         (let ((newvar (make-var var)))
  406.           (setq form (subst newvar var form))
  407.           (push newvar newvars)
  408.       ) )
  409.       (setq newvars (nreverse newvars))
  410.       (catch 'main-cut-tag
  411.         (prolog-form form
  412.           #'(lambda () (setq foundsome t) (print-bindings vars newvars))
  413.           'main-cut-tag
  414.       ) )
  415.       (format t "~%No ~:[~;other ~]solutions." foundsome)
  416.   ) )
  417.   (values)
  418. )
  419.  
  420. (defmacro goal (vars form)
  421.   `(time (goal1 ',vars ',form))
  422. )
  423.  
  424.