forked from Anniepoo/amziexpertsystemsinprolog
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nativeshell.pl
214 lines (178 loc) · 5.31 KB
/
nativeshell.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
% Native - a simple shell for use with Prolog
% knowledge bases. It includes expanations.
% :- module(nativeshell, [start_shell/0, ask/2,
% ask/3,menuask/3,menuask/4]).
:-op(900,xfy, :).
% for SWI-Prolog
:- initialization(start_shell). % start shell on load
:- dynamic known/3, multivalued/1.
% SWI-Prolog defines main/0
% so I've renamed this to start_shell
%start_shell :-
% greeting,
% repeat,
% write('> '),
% read(X),
% do(X),
% X == quit.
start_shell :-
greeting,
repeat,
(
write('> '),
read(X),
(
do(X),
X = quit, ! % prune and succeed, terminate repeat
;
fail % fail, don't show 'true', and repeat shell
)
).
greeting :-
write('This is the native Prolog shell.'), nl,
native_help.
do(help) :- native_help, !.
do(load) :- load_kb, !.
do(solve) :- solve, !.
do(how(Goal)) :- how(Goal), !.
do(whynot(Goal)) :- whynot(Goal), !.
do(quit).
do(X) :-
write(X),
write(' is not a legal command.'), nl,
fail.
native_help :-
write('Type help. load. solve. how(Goal). whynot(Goal). or quit.'),nl,
write('at the prompt.'), nl.
load_kb :-
write('Enter file name in single quotes (ex. ''birds.nkb''.): '),
read(F),
consult(F).
solve :-
retractall(known(_,_,_)),
prove(top_goal(X),[]),
write('The answer is '),write(X),nl.
solve :-
write('No answer found.'),nl.
% not sure what's up with Hist
% this is workaround
ask(Attribute,Value) :-
ask(Attribute, Value, []).
ask(Attribute,Value,_) :-
known(yes,Attribute,Value), % succeed if we know its true
!. % and dont look any further
ask(Attribute,Value,_) :-
known(_,Attribute,Value), % fail if we know its false
!, fail.
ask(Attribute,_,_) :-
\+ multivalued(Attribute), % for SWI-Prolog
known(yes,Attribute,_), % fail if its some other value.
!, fail. % the cut in clause #1 ensures
% this is the wrong value
ask(A,V,Hist) :-
write(A :V), % if we get here, we need to ask.
write('? (yes or no) '),
get_user(Y,Hist), % get the answer
asserta(known(Y,A,V)), % remember it so we dont ask again.
Y = yes. % succeed or fail based on answer.
% not sure whats up with Hist
menuask(Attribute, Value, Menu) :-
menuask(Attribute, Value, Menu, []).
% "menuask" is like ask, only it gives the user a menu to to choose
% from rather than a yes on no answer. In this case there is no
% need to check for a negative since "menuask" ensures there will
% be some positive answer.
menuask(Attribute,Value,_,_) :-
known(yes,Attribute,Value), % succeed if we know
!.
menuask(Attribute,_,_,_) :-
known(yes,Attribute,_), % fail if its some other value
!, fail.
menuask(Attribute,AskValue,Menu,Hist) :-
nl,write('What is the value for '),write(Attribute),write('?'),nl,
display_menu(Menu),
write('Enter the number of choice> '),
get_user(Num,Hist),nl,
pick_menu(Num,AnswerValue,Menu),
asserta(known(yes,Attribute,AnswerValue)),
AskValue = AnswerValue. % succeed or fail based on answer
display_menu(Menu) :-
disp_menu(1,Menu), !. % make sure we fail on backtracking
disp_menu(_,[]).
disp_menu(N,[Item | Rest]) :- % recursively write the head of
write(N),write(' : '),write(Item),nl, % the list and disp_menu the tail
NN is N + 1,
disp_menu(NN,Rest).
pick_menu(N,Val,Menu) :-
integer(N), % make sure they gave a number
pic_menu(1,N,Val,Menu), !. % start at one
pick_menu(Val,Val,_). % if they didn't enter a number, use
% what they entered as the value
pic_menu(_,_,none_of_the_above,[]). % if we've exhausted the list
pic_menu(N,N, Item, [Item|_]). % the counter matches the number
pic_menu(Ctr,N, Val, [_|Rest]) :-
NextCtr is Ctr + 1, % try the next one
pic_menu(NextCtr, N, Val, Rest).
get_user(X,Hist) :-
repeat,
write('> '),
read(X),
process_ans(X,Hist), !.
process_ans(why,Hist) :-
write_list(4,Hist), !, fail.
process_ans(_,_).
% Prolog in Prolog for explanations.
% It is a bit confusing because of the ambiguous use of the comma, both
% to separate arguments and as an infix operator between the goals of
% a clause.
prove(true,_) :- !.
prove((Goal,Rest),Hist) :-
prov(Goal,[Goal|Hist]),
prove(Rest,Hist).
prove(Goal,Hist) :-
prov(Goal,[Goal|Hist]).
prov(true,_) :- !.
prov(menuask(X,Y,Z),Hist) :- menuask(X,Y,Z,Hist), !.
prov(ask(X,Y),Hist) :- ask(X,Y,Hist), !.
prov(Goal,Hist) :-
clause(Goal,Body),
prove(Body,Hist).
% Explanations
how(Goal) :-
clause(Goal,Body),
prove(Body,[]),
write_body(4,Body).
whynot(Goal) :-
clause(Goal,Body),
write_line([Goal,'fails because: ']),
explain(Body).
whynot(_).
explain(true).
explain((Head,Body)) :-
check(Head),
explain(Body).
check(H) :- prove(H,[]), write_line([H,succeeds]), !.
check(H) :- write_line([H,fails]), fail.
write_list(_N,[]).
write_list(N,[H|T]) :-
tab(N),write(H),nl,
write_list(N,T).
write_body(N,(First,Rest)) :-
tab(N),write(First),nl,
write_body(N,Rest).
write_body(N,Last) :-
tab(N),write(Last),nl.
write_line(L) :-
flatten(L,LF),
write_lin(LF).
write_lin([]) :- nl.
write_lin([H|T]) :-
write(H), tab(1),
write_lin(T).
flatten([],[]) :- !.
flatten([[]|T],T2) :-
flatten(T,T2), !.
flatten([[X|Y]|T], L) :-
flatten([X|[Y|T]],L), !.
flatten([H|T],[H|T2]) :-
flatten(T,T2).