1
0

eliza.lisp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. (in-package #:chatikbot)
  2. (defparameter *eliza-rules*
  3. '((((?* ?x) hello (?* ?y))
  4. (How do you do. Please state your problem.))
  5. (((?* ?x) want (?* ?y))
  6. (What would it mean if ?x got ?y)
  7. (Why do ?x want ?y)
  8. (Suppose ?x got ?y soon))
  9. (((?* ?x) if (?* ?y))
  10. (Do you really think its likely that ?y)
  11. (Do you wish that ?y)
  12. (What do you think about ?y)
  13. (Really-- if ?y))
  14. (((?* ?x) no (?* ?y))
  15. (Why not?)
  16. (You are being a bit negative)
  17. (Are you saying "NO" just to be negative?))
  18. (((?* ?x) I was (?* ?y))
  19. (Were you really?)
  20. (Perhaps I already knew you were ?y)
  21. (Why do you tell me you were ?y now?))
  22. (((?* ?x) I feel (?* ?y))
  23. (Do you often feel ?y ?))
  24. (((?* ?x) I felt (?* ?y))
  25. (What other feelings do you have?))
  26. (((?* x))
  27. (Why would you say that?))))
  28. (defconstant fail nil "Indicates pat-match failure")
  29. ;; (defconstant no-bindings '((t . t))
  30. ;; "Indicates pat-match success, with no variables.")
  31. (defun get-binding (var bindings)
  32. "Find a (variable . value) pair in a binding list."
  33. (assoc var bindings))
  34. (defun binding-val (binding)
  35. "Get the value part of a single binding."
  36. (cdr binding))
  37. (defun lookup (var bindings)
  38. "Get the value part (for var) from a binding list."
  39. (binding-val (get-binding var bindings)))
  40. (defun extend-bindings (var val bindings)
  41. "Add a (var . value) pair to a binding list."
  42. (cons (cons var val) bindings))
  43. (defun match-variable (var input bindings)
  44. "Does VAR match input? Uses (or updates) and returns bindings."
  45. (let ((binding (get-binding var bindings)))
  46. (cond ((not binding) (extend-bindings var input bindings))
  47. ((equal input (binding-val binding)) bindings)
  48. (t fail))))
  49. (defun variable-p (x)
  50. "Is x a variable (a symbol beginning with '?')?"
  51. (and (symbolp x) (equal (char (symbol-name x) 0) #\?)))
  52. (defun starts-with (list x)
  53. "Is this a list whose first element is x?"
  54. (and (consp list)
  55. (eql (first list) x)))
  56. (defun segment-pattern-p (pattern)
  57. "Is this a segment matching pattern: ((?* var) . pat)"
  58. (and (consp pattern)
  59. (starts-with (first pattern) '?*)))
  60. (defun pat-match (pattern input &optional (bindings no-bindings))
  61. "Match pattern against input in the context of the bindings"
  62. (cond ((eq bindings fail) fail)
  63. ((variable-p pattern)
  64. (match-variable pattern input bindings))
  65. ((eql pattern input) bindings)
  66. ((segment-pattern-p pattern)
  67. (segment-match pattern input bindings))
  68. ((and (consp pattern) (consp input))
  69. (pat-match (rest pattern) (rest input)
  70. (pat-match (first pattern) (first input)
  71. bindings)))
  72. (t fail)))
  73. (defun segment-match (pattern input bindings &optional (start 0))
  74. "Match the segment pattern ((?* var) . pat) against input."
  75. (let ((var (second (first pattern)))
  76. (pat (rest pattern)))
  77. (if (null pat)
  78. (match-variable var input bindings)
  79. ;; We assume that pat starts with a constant
  80. ;; In other words, a pattern can't have 2 consecutive vars
  81. (let ((pos (position (first pat) input
  82. :start start :test #'equal)))
  83. (if (null pos)
  84. fail
  85. (let ((b2 (pat-match
  86. pat (subseq input pos)
  87. (match-variable var (subseq input 0 pos)
  88. bindings))))
  89. ;;If this match failed, try another longer one
  90. (if (eq b2 fail)
  91. (segment-match pattern input bindings (+ pos 1))
  92. b2)))))))
  93. (defun punctuation-p (char)
  94. (find char ".,;:'!?#-()\\\""))
  95. (defun read-from-string-no-punct (input)
  96. "Read from an input string, ignoring punctuation."
  97. (read-from-string
  98. (concatenate 'string
  99. "("
  100. (substitute-if #\space #'punctuation-p input)
  101. ")")))
  102. (defun print-with-spaces (list)
  103. (string-downcase (format nil "~{~a~^ ~}" list)))
  104. (defun mappend (fn &rest lists)
  105. "Apply fn to each element of lists and append the results."
  106. (apply #'append (apply #'mapcar fn lists)))
  107. (defun random-elt (choices)
  108. "Choose an element from a list at random."
  109. (elt choices (random (length choices))))
  110. (defun flatten (the-list)
  111. "Append together elements (or lists) in the list."
  112. (mappend #'(lambda (x) (if (listp x) (flatten x) (list x))) the-list))
  113. (defun switch-viewpoint (words)
  114. "Change I to you and vice versa, and so on."
  115. (sublis '((I . you) (you . I) (me . you) (am . are))
  116. words))
  117. (defun rule-pattern (rule)
  118. (first rule))
  119. (defun rule-responses (rule)
  120. (rest rule))
  121. (defun use-eliza-rules (input)
  122. "Find some rule with which to transform the input."
  123. (some #'(lambda (rule)
  124. (let ((result (pat-match (rule-pattern rule) input)))
  125. (unless (eq result fail)
  126. (sublis (switch-viewpoint result)
  127. (random-elt (rule-responses rule))))))
  128. *eliza-rules*))
  129. (defun eliza (input)
  130. (let ((response (use-eliza-rules
  131. (read-from-string-no-punct input))))
  132. (when response
  133. (print-with-spaces (flatten response)))))