% From the book % PROLOG PROGRAMMING IN DEPTH % by Michael A. Covington, Donald Nute, and Andre Vellino % (Prentice Hall, 1997). % Copyright 1997 Prentice-Hall, Inc. % For educational use only % XSHELL.PL % % An expert system consultation driver to be used % with separately written knowledge bases. % % Procedures in the file include XSHELL, XSHELL_AUX, % FINISH_XSHELL, PROP, PARM, PARMSET, PARMRANGE, % EXPLAIN, MEMBER, and WAIT. % % Requires various procedures defined in the files % READSTR.PL, READNUM.PL, and GETYESNO.PL from % Chapter 5. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% question2(Result,Question):- jpl_datums_to_array(Question,Q), jpl_new('miw2.YNQuestion',[Q],D), jpl_call(D,setVisible,[@(true)],_), jpl_call('miw2.YNQuestion',getValue,[],V), Result = V. info(X):- jpl_datums_to_array(X,Q), jpl_new('miw2.Info',[Q],_). myQuestion(Question,Choices,Response):- jpl_datums_to_array(Choices,L), jpl_datums_to_array(Question,Q), jpl_new('miw2.MyRBForm',[L,Q],F), jpl_call(F,setVisible,[@(true)],_), jpl_call('miw2.MyRBForm',getValue,[],V), Response = V. change(0,y). change(1,n). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% :- dynamic known/2. :- ensure_loaded('readstr.pl'). :- ensure_loaded('readnum.pl'). :- ensure_loaded('writeln.pl'). % :- ensure_loaded('getyesno.pl'). % % xshell % The main program or procedure for the expert system % consultation driver. It always succeeds. % xshell :- xkb_intro(Statement), info(Statement), wait, xkb_identify(RULE,TextList), asserta(known(identification,RULE)), append_list(TextList,Text), info(Text), explain, xkb_unique(yes([''])), !, xshell_aux. xshell :- xshell_aux. % % xshell_aux % Prevents an abrupt end to a consultation that ends % without an identification, or a consultation where % multiple identifications are allowed. % xshell_aux :- \+ known(identification,_), info(['I cannot reach a conclusion.']), !, finish_xshell. xshell_aux :- xkb_unique(no), known(identification,_), info(['I cannot reach any further conclusion.']), !, finish_xshell. xshell_aux :- finish_xshell. % % finish_xshell % Erases the working database and asks if the user wants % to conduct another consultation. % finish_xshell :- retractall(known(_,_)), yes(['Do you want to conduct another consultation?']), !, xshell. finish_xshell. % % prop(+Property) % Succeeds if it is remembered from an earlier call that % the subject has the Property. Otherwise the user is % asked if the subject has the Property and the user's % answer is remembered. In this case, the procedure call % succeeds only if the user answers 'yes'. % prop(Property) :- known(Property,Value), !, Value == y. prop(Property) :- xkb_question(Property,Question,_,_), yes(Question), !, assert(known(Property,Result)). prop(Property) :- assert(known(Property,n)), !, fail. % % parm(+Parameter,+Type,+Value) % Type determines whether Value is to be a menu choice, an % atom, or a number. Value becomes the remembered value % for the parameter if there is one. Otherwise the user is % asked for a value and that value is remembered. Calls to % this procedure are used as test conditions for identification % rules. Value is instantiated before the procedure is called % and parm(Parameter,Type,Value) only succeeds if the remembered % value, or alternatively the value reported by the user, % matches Value. % parm(Parameter,_,Value) :- known(Parameter,StoredValue), !, Value = StoredValue. parm(Parameter,m,Value) :- xkb_menu(Parameter,Header,Choices,_), length(Choices,L), myQuestion(Header,Choices,N), readnumber_in_range(1,L,N), assert(known(Parameter,N)), !, Value = N. parm(Parameter,a,Value) :- xkb_question(Parameter,Question,_,_), % writeline(Question), question(Response,Question), assert(known(Parameter,Response)), !, Value = Response. parm(Parameter,n,Value) :- xkb_question(Parameter,Question,_,_), question(Response,Question), assert(known(Parameter,Response)), !, Value = Response. % % parmset(+Parameter,+Type,+Set) % Type indicates whether the Parameter takes a character, % an atom, or a number as value, and Set is a list of % possible values for Parameter. A call to the procedure % succeeds if a value for Parameter is established that is % a member of Set. % parmset(Parameter,Type,Set) :- parm(Parameter,Type,Value), member(Value,Set). % % parmrange(+Parameter,+Minimum,+Maximum) % Parameter must take numbers as values, and Minimum and % Maximum must be numbers. A call to the procedure succeeds % if a value for Parameter is established that is in the % closed interval [Minimum,Maximum]. % parmrange(Parameter,Minimum,Maximum) :- parm(Parameter,n,Value), Minimum =< Value, Maximum >= Value. % % explain and explain_aux % Upon request, provide an explanation of how an % identification was made. % explain :- xkb_explain(no), wait, !. explain :- \+ yes(['Do you want to see the rule that was used to reach the conclusion?']),!. explain :- known(identification,RULE), clause(xkb_identify(RULE,_),Condition), info([ % info([{RULE}]), 'Reach this conclusion IF']), explain_aux(Condition), wait, !. explain_aux((Condition,RestOfConditions)) :- !, interpret(Condition), explain_aux(RestOfConditions). explain_aux(Condition) :- interpret(Condition). % % interpret(+Condition). % Uses questions and menus associated with a condition of % and identification rule to display the condition in a % format that makes sense to the user. % interpret(prop(Property)) :- !, xkb_question(Property,_,Text,_), % Text is a message that says the subject to be % identified has the Property. info([Text]). interpret(\+(prop(Property))) :- !, xkb_question(Property,_,_,Text), % Text is a message that says the subject to be % identified does not have the Property. info([Text]). interpret(parm(Parameter,m,N)) :- !, xkb_menu(Parameter,_,Choices,Prefix), % Prefix is a phrase that informs the user which % Parameter is involved. nth_member(N,Text,Choices), % nth_member is used to retrieve the user's choice % from the menu associated with the Parameter. info([Prefix, Text, '.']). interpret(\+(parm(Parameter,m,N))) :- !, xkb_menu(Parameter,_,Choices,Prefix), % Prefix is a phrase that informs the user which % Parameter is involved. nth_member(N,Text,Choices), % nth_member is used to retrieve the user's choice % from the menu associated with the Parameter. info([Prefix, 'NOT ',Text,'.']). interpret(parm(Parameter,_,Value)) :- !, % For any Parameter whose Value is not obtained % by using a menu. xkb_question(Parameter,_,Prefix,_), info([Prefix,Value,'.']). interpret(\+(parm(Parameter,_,Value))) :- !, % For any Parameter whose Value is not obtained % by using a menu. xkb_question(Parameter,_,Prefix,_), info([Prefix,' NOT ',Value,'.']). interpret(parmset(Parameter,m,Set)) :- !, xkb_menu(Parameter,_,Choices,Prefix), info([Prefix,' one of the following -']), % Since parmset is involved, any value for Parameter % included in Set would have satisfied the condition. list_choices_in_set(Set,Choices,1). interpret(\+(parmset(Parameter,m,Set))) :- !, xkb_menu(Parameter,_,Choices,Prefix), info([Prefix,' NOT one of the following -']), % Since parmset is involved, any value for Parameter % not in Set would have satisfied the condition. list_choices_in_set(Set,Choices,1). interpret(parmset(Parameter,_,Set)) :- !, % For any Parameter whose Value is not obtained % by using a menu. xkb_question(Parameter,_,Prefix,_), info([Prefix,' one of the following - ']), enumerate(Set,1). interpret(\+(parmset(Parameter,_,Set))) :- !, % For any Parameter whose Value is not obtained % by using a menu. xkb_question(Parameter,_,Prefix,_), info([Prefix, ' NOT one of the following - ']), enumerate(Set,1). interpret(parmrange(Parameter,Min,Max)) :- !, xkb_question(Parameter,_,Prefix,_), info([Prefix, ' between ', Min,' and ',Max, '.']). interpret(\+(parmrange(Parameter,Min,Max))) :- !, xkb_question(Parameter,_,Prefix,_), info([Prefix,' NOT between ', Min,' and ', Max, '.']). interpret(\+(Condition)) :- clause(Condition,Conditions), % Any condition that does not have prop, parm, % parmset, or parmrange as its functor must corres- % pond to some Prolog rule with conditions of its % own. Eventually, all conditions must terminate in % conditions using prop, parm, parmset, or parmrange. info(['A condition between here and "end" is NOT satisfied -']), explain_aux(Conditions), info([' end']). interpret(Condition) :- clause(Condition,Conditions), % Any condition that does not have prop, parm, % parmset, or parmrange as its functor must corres- % pond to some Prolog rule with conditions of its % own. Eventually, all conditions must terminate in % conditions using prop, parm, parmset, or parmrange. explain_aux(Conditions). % % enumerate(+N,+X) % Prints each atom in list X on a separate line, numbering % the atoms beginning with the number N. Used to enumerate % menu choices. % enumerate([],_). enumerate([H|T],N) :- info([N,'. ',H]), M is N + 1, enumerate(T,M). % % list_choices_in_set(+X,+Y,+N) % The members of the list of atoms Y corresponding to the % positions in the list indicated by the members of the list % of integers X are printed on separate lines and numbered % beginning with the number N. % list_choices_in_set([],_,_). list_choices_in_set([N|Tail],Choices,M) :- nth_member(N,Choice,Choices), info([M,'. ',Choice]), K is M + 1, list_choices_in_set(Tail,Choices,K). % % readnumber_in_range(+Min,+Max,-Response) % Evokes a numerical input from the user which must be % between Min and Max inclusively. % readnumber_in_range(Min,Max,Response) :- % readnumber(Num), testnumber_in_range(Min,Max,Num,Response). % % testnumber_in_range(+Min,+Max,+Input,-Response) % Tests user Input to insure that it is a number between % Min and Max inclusively. If it is not, instructions for % the user are printed and readnum/1 is called to accept % another numerical input from the user. % testnumber_in_range(Min,Max,Num,Num) :- Min =< Num, Num =< Max, !. testnumber_in_range(Min,Max,_,Num) :- info(['Number between ', Min, ' and ', Max, ' expected. Try again. ']), readnumber_in_range(Min,Max,Num). % % wait % Stops execution until the user presses a key. Used to % prevent information from scrolling off the screen before % the user can read it. % wait :- info(['Press Return when ready to continue. ']). % % yes % Prompts the user for a response and succeeds if the % user enters 'y' or 'Y'. % yes(Question) :- question2(Response,Question), !, Response == yes. member(X,[X|_]). member(X,[_|Y]) :- member(X,Y). % % nth_member(+N,-X,+Y) % X is the nth element of list Y. % nth_member(1,X,[X|_]). nth_member(N,X,[_|Y]) :- nth_member(M,X,Y), N is M + 1. append_list([],[]). append_list([N|Tail],Text) :- append_list(Tail,Text1), xkb_text(N,Text2), append(Text2,Text1,Text).