How to know which questions to ask on front end for rule based system - rule-engine

I am working on a simple diagnosis rule based expert system. It should ask questions and recognize animal health problems. I am using backward chaining for reasoning. How do i know which questions to ask on front end for asserting new rules? Lets say i have a bunch of rules IF A THEN B, IF B THEN C. Know it will check for C if B is asserted, then it will check if A is asserted. Now since a isn't asserted, i need to ask the question on front end. Is there some methodology for knowing what question to ask?

It largely depends on the details of how the backward chaining is implemented. For example, here's how you can do it in Jess where the engine generates goals that can be matched by rules:
Jess>
(deftemplate symptom
(declare (backchain-reactive TRUE))
(slot name)
(slot value))
TRUE
Jess>
(deftemplate diagnosis
(slot name))
TRUE
Jess>
(deftemplate question
(slot name)
(slot string))
TRUE
Jess>
(deffacts questions
(question (name has-fever) (string "Does patient have a fever?"))
(question (name swollen-neck) (string "Does patient have a swollen neck?"))
(question (name skin-rash) (string "Does patient have a skin rash?")))
TRUE
Jess>
(defrule measles
(symptom (name has-fever) (value yes))
(symptom (name skin-rash) (value yes))
=>
(assert (diagnosis (name measles)))
(printout t "Patient has measles." crlf))
TRUE
Jess>
(defrule mumps
(symptom (name has-fever) (value yes))
(symptom (name swollen-neck) (value yes))
=>
(assert (diagnosis (name mumps)))
(printout t "Patient has mumps." crlf))
TRUE
Jess>
(defrule ask-question
(need-symptom (name ?name))
(question (name ?name) (string ?string))
(not (diagnosis))
=>
(printout t ?string " ")
(assert (symptom (name ?name) (value (read)))))
TRUE
Jess> (reset)
TRUE
Jess> (run)
Does patient have a fever? yes
Does patient have a swollen neck? yes
Patient has mumps.
3
Jess> (reset)
TRUE
Jess> (run)
Does patient have a fever? yes
Does patient have a swollen neck? no
Does patient have a skin rash? yes
Patient has measles.
4
Jess>

Related

Why are calls to `defmacro` evaluating to None?

I was writing the following pieces of code:
(require [hy.contrib.walk [let]])
(defn maybe? [command-text]
(let [splitted (.split command-text " ")]
(= (get splitted 0) "maybe")))
(defn if? [command-text]
(let [splitted (.split command-text " ")]
(+ (get splitted 0) "if")))
... until I realized I was doing something repetitive, so I wanted to factor out the pattern:
(import [hy [HySymbol]])
(defmacro command-dispatcher [command-type]
`(defn ~(HySymbol (+ command-type "?")) [command-text]
(let [splitted (.split command-text " ")]
(= (get splitted 0) ~command-type))))
However, if I evaluate (command-dispatcher "maybe") in HyREPL, I get a None.
=> (command-dispatcher "maybe")
def is_maybe(command_text):
_hyx_letXUffffX3 = {}
_hyx_letXUffffX3['splitted'] = command_text.split(' ')
return _hyx_letXUffffX3['splitted'][0] == 'maybe'
None
This is weird, because a macro should return a HyExpression, not None. What am I missing?
Your macro will not return anything but will define a function, as you can see here
(assert (not (in "is_maybe" (dir))))
(command-dispatcher "maybe")
(assert (in "is_maybe" (dir)))
An issue in your code is that you are using let, which is not available anymore according to documentation, here is a possible way to rewrite it using setv instead:
(defmacro command-dispatcher [command-type]
`(defn ~(HySymbol (+ command-type "?")) [command-text]
(setv splitted (.split command-text " "))
(= (get splitted 0) ~command-type)))
You can then call this function using is_maybe (or maybe?, that's syntactic sugar), eg.
(command-dispatcher "maybe")
(print (maybe? "foo"))
(print (maybe? "maybe foo"))
Will print
False
True

why "and" can not be operation within accumulate in Racket-lang?

#lang racket
(define (accumulate op initial sequence)
(if (null? sequence)
initial
(op (car sequence)
(accumulate op initial (cdr sequence)))))
(define (all-is-true? items)
(accumulate and
true
items))
(all-is-true? (list true true true))
the output is:
and: bad syntax in: and
I cannot find why the "and" procedure cannot be the operation of accumulate.
and is a macro. In this example you can fix your code by writing:
(define (all-is-true? items)
(accumulate (lambda (a b) (and a b))
true
items))
Macros can not be passed as arguments. They can only appear as the car of an application.

Comparing a number and a list object in lisp

I am new to lisp so I apologize if advance it this is a simple question.
I have a list:
(set ‘inventory ‘(parts
((item 1001) (shoes (color brown) (size 10) (cost 20)))
((item 2011) (skirt (color blue) (size 4) (cost 10)))
((item 2120) (pants (color white) (size 32) (cost 30)))
((item 2121) (pants (color brown) (size 34) (cost 30)))))
I am trying to write a function that looks through the list by item number and return the correct one.
(findItem 1001 inventory) should return:
((item 1001) (shoes (color brown) (size 10) (cost 20)))
This is what I have so far:
(defun findItem (q i)
(cond
((null q)
nil)
((null (cdr i))
nil)
((eq `parts (car i))
(findItem q (cdr i)))
((eq q (cdr (car (car i))))
(car i))
(T
(findItem q (cdr i)))))
Everything seems to work except ((eq q (cdr (car (car i)))) (car i))
(cdr (car (car i))) should return (1001) or the part number
But the eq does not evaluate to true so the function overall returns nil.
And help would be appreciated
As explained in What's the difference between eq, eql, equal, and equalp in Common Lisp?, using
eq to compare
cons cells is wrong.
You should either use equal or extract
the number, i.e., replace (cdr (car (car i))) with
cadaar so that
you will get 1001 instead of (1001).
Also, set is deprecated, use
setq or
defparameter instead.
Also, you can use built-in
find instead:
(find 1001 (cdr inventory) :key #'cadar)
==> ((ITEM 1001) (SHOES (COLOR BROWN) (SIZE 10) (COST 20)))
(cdr (car (car '((item 1001) (shoes (color brown) (size 10) (cost 20))))))
Doesn't work since caar is item and you cannot take the cdr of a symbol.
You should play with it like this before you get it right:
(defparameter *item* '((item 1001) (shoes (color brown) (size 10) (cost 20))))
(car *item*) ; ==> (item 1001)
(caar *item*) ; ==> item
(cdar *item*) ; ==> (1001)
Now c<runs of a and d>r is an abbriviation for nested car and cdr eg (car (cdr (cdr x))) (also known as third) can be abbriviated caddr when reading the as and ds from right to left you see why.
Numbers are not guaranteed to be eq even when they represent the same value. Tou can use equal to get a true value when two values look alike and there are = which only works for numbers that might be faster in some implementations.
set is deprecated. For making global variables use defparameter and use *earmuffs* to indicate that they are global variables.
I urge you to chose sensible names as q and i doesn't really mean anything. Also using CL coding conversion make your code concise and readable by other lispers. Eg:
(defun find-item (item-number inventory)
"Finds the record in inventory that has a specific item number"
(cond ((endp inventory) nil)
...))
To extend on sds's answer, let me address some stylistic issues. Like in any other language, you should try to design your code to define a sensible interface.
I could tell you that you might prefer to use structures or objects, but I actually think that the use of lists is not necessarily bad. What bothers me is the explicit use of CADAR or CDR: you are making assumptions about your code which introduces coupling for no particular reason. A first step to separate abstraction from implementation is to define accessor functions, like item-reference, row-reference (for each entry), and so on.
If you happen to reorganize your data, you will be happy to know that you only need to change the one place where you define (item-reference x) to be (CAADDR x) (for example).
By the way, Common Lisp structures can be implemented on top of lists if you provide both :named and a :type list argument. For example, you can define an item structure as follows:
> (defstruct (item (:type list) :named) reference)
Then, the following actually build a list where the first element indicates the type:
> (make-item :reference 300)
=> (ITEM 300)
The defstruct also creates accessors:
> (item-reference *)
300
> (setf (item-reference **) 1010)
1010
What you have there is an alist. You should leverage the existing functions that are built in for working with this data structure.
(defun find-item (num list)
(assoc num (cdr list)
:test (lambda (num b)
(equal num (second b)))))
Note that I have also changed the name of your function to be more in keeping with list style.

find the average using jess or clips

This is clips code to find the average ... but it dose not work ... can anyone know how to fix it ??
(deftemplate fact (slot name) (slot value))
(deftemplate avg (slot result))
(deffacts data
(fact (name data-1) (value 3))
(fact (name data-2) (value 1))
(fact (name data-3) (value 2))
(fact (name data-4) (value 2))
(fact (name data-5) (value 4))
(fact (name data-6) (value 3)))
(deffact sum (avg (result 0))
(defrule find-avg
(fact (name ?name1) (value ?value1))
(avg (modify (result (+ result ?value1))
))
(defrule finding-avg
(avg (modify (result (/ result 6)))
))
In CLIPS, you can use the fact query functions to do this:
CLIPS> (deftemplate fact (slot name) (slot value))
CLIPS> (deftemplate avg (slot result))
CLIPS>
(deffacts data
(fact (name data-1) (value 3))
(fact (name data-2) (value 1))
(fact (name data-3) (value 2))
(fact (name data-4) (value 2))
(fact (name data-5) (value 4))
(fact (name data-6) (value 3)))
CLIPS>
(deffunction average (?template ?slot)
(bind ?sum 0)
(bind ?count 0)
(do-for-all-facts ((?f ?template)) TRUE
(bind ?sum (+ ?sum (fact-slot-value ?f ?slot)))
(bind ?count (+ ?count 1)))
(if (= ?count 0)
then FALSE
else (/ ?sum ?count)))
CLIPS> (reset)
CLIPS> (assert (avg (result (average fact value))))
<Fact-7>
CLIPS> (facts)
f-0 (initial-fact)
f-1 (fact (name data-1) (value 3))
f-2 (fact (name data-2) (value 1))
f-3 (fact (name data-3) (value 2))
f-4 (fact (name data-4) (value 2))
f-5 (fact (name data-5) (value 4))
f-6 (fact (name data-6) (value 3))
f-7 (avg (result 2.5))
For a total of 8 facts.
CLIPS>
You have to use accumulate to count and sum slot value over all fact facts.
(defrule find-avg
?avg <- (accumulate
(progn (bind ?sum 0)(bind ?count 0))
(progn (bind ?sum (+ ?sum ?value))(++ ?count))
(/ ?sum ?count)
(fact (value ?value)))
=>
(printout t "average " ?avg crlf)
)
progn is required for grouping function calls. Functions progn, bind etc. as well as accumulate is all very well documented in the Jess manual so I'm not going to repeat that here.

How to map a macro over a list - or - How to use macros to define data types

I like to build a REPL with my own datatypes, but I don't like to write all the same pattern functions over and over again.
So this is a nut, which bothers me.
I got my own set of primitive datatypes (define primitives '("mytrue" "myfalse" "mynumber" ...))
Also I have (define primitiveTesters (list "mytrue?" "myfalse?" "mynumber?" ... )
The problem now is, I just want to apply (map) or a macro to get the datatype? procedurces, which basically just checks if the car of record (mynumber . ( . )) exists.
So something similar like (mynumber? (car (mynumber.(1.))) => #t in the end. But for this I need (define mynumber? (lambda (...)(...))
My define-batching macro looks like this, but I just have no luck to infuse the <variable>.
(define-syntax define-batching
(syntax-rules ()
((_ value expr)(define value expr))
((_ value) value)
((_ value1 value2 ...) (begin (define value1 expr) (define-batching test2...)))
))
So have I reached a dead end of scheme ?
I've seen something similar, I think in Emacs Lisp.
What I am looking for in the end is:
(define checker '(audi? volkswagen? mercedes?))
(define datatype '(audi volkswagen mercedes))
(map define-checker checker datatype )
or
(define-checker (car checker) (car datatype))
If I understood the question right, you need a macro
to define your own type checkers?
Here is one way to do it:
(define-syntax define-checker
(syntax-rules ()
[(define-checker name tag)
(define (name object)
(and (list? object)
(not (null? object))
(eq? (car object) 'tag)))]))
(define-checker my-car? car)
(my-car? '(car audi black)) ; evaluates to #t
(my-car? '(truck ford pink)) ; evaluates to #f
Addendum:
If you write
(define checker '(audi? volkswagen? mercedes?))
(define datatype '(audi volkswagen mercedes))
the values will become available at runtime.
Therefore you need to a different approach.
You could for example write:
(define-checker+datatype (audi? audi) (volkswagen? volkswagen?))
Here is the code:
(define-syntax define-checker
(syntax-rules ()
[(define-checker name tag)
(define (name object)
(and (list? object)
(not (null? object))
(eq? (car object) 'tag)))]))
(define-syntax define-checkers+datatype
(syntax-rules ()
[(define-checkers+datatype (name tag) ...)
(begin
(define-checker name tag)
...)]))
(define-checkers+datatype (audi? audi) (wv? wv))
(audi? '(audi black))
define-syntax is hygienic, that means it cannot influence on parent environment, that means it cannot define symbols in it.
You may try to use er-, ir- macro-transformers which allow you to explicit renames symbols.
keywords to google in you scheme documentation are 'er-macro-transformet' and 'ir-macro-transformer'