Related
Lets assume I have facts as follows:
airport(nyc,'newyork').
I want want to display a message if the user inputs an airport that doesn't exist.
My Attempt:
isAirport(Air) :-
airport(Air,T),
(var(T) -> true
;
write('Airport not found'),
fail
).
However, this doesn't seem to work.
First let's see what happens if you query a conjunction (the , operator) first:
?- airport(nyc, _), write('found!').
found!
true.
?- airport(abc, _), write('found!').
false.
This means, isAirport(abc) directly fails after trying airport(abc,_) without the rest of your predicate being evaluated. In many cases, you can therefore get by without an explicit if-then-else construct and just write something of the form
predicate(X) :-
first_condition(X),
second_condition(X).
and it will only succeed if both conditions are fulfilled for X.
In case you really want to create some user interface, this is a bit more tricky, because I/O is inherently non-logical, in particular when there is backtracking involved. We usually call a program which behaves like we would expect from a logical formula pure and when it contains non-logical constructs like I/O or the cut operator ! are called impure.
Unfortunately, the if-then-else construct (-> and ;) and negation (\+) are implemented via cut and therefore impure as well. Luckily, most of the time people want a conditional, a pure disjunction is sufficient:
case(1,a).
case(2,b).
We have an automatic branching from the execution mechanism of Prolog:
?- case(X,Y).
X = 1,
Y = a ;
X = 2,
Y = b.
But sometimes we really want to do something that needs the impure constructs, like user input. Then the easiest way to keep the nice logical properties of our program is to separate the task into pure and impure ones:
main :-
uinput(Data),
pure_predicate(Data, Result),
write(Result).
After we have done all the impure parts, Data is unified with the user data we wanted. Let's have a look at the implementation of uinput/1:
uinput(data(Airport,D-M-Y)) :-
format('~nAirport? '),
read(Airport),
( ground(Airport), airport(Airport, _) )
->
(
format('~nDay? '),
read(D),
format('~nMonth? '),
read(M),
format('~nYear? '),
read(Y),
( ground(D-M-Y), isDate(D-M-Y) )
->
true
;
throw(failure('unknown date'))
)
;
throw(failure('unknown airport'))
.
We successively read terms from the input and throw an exception if we can't handle it. For the if-then-else construct to work, we need to take special care. If we compare the two queries:
?- between(1,3,X), write(X).
1
X = 1 ;
2
X = 2 ;
3
X = 3.
and
?- between(1,3,X) -> write(X); false.
1
X = 1.
you can see that the if-then-else is losing solutions. This means we need to make sure that our condition is deterministic. Asking for a user input term to be ground is already a good idea, because without variables, there is only one solution term. Still, a call to one of the data-predicates airport/1 and isDate/1 might generate the same term multiple times or not terminate at all. In this particular case, we just need to make sure that each airport has a unique shortcut name, we can also generate dates without repetition:
airport(nyc, 'New York City').
airport(wdc, 'Washington DC').
isDate(X-Y-Z) :-
between(1,31,X),
between(1,12,Y),
between(1970,2100,Z).
Another trick in the implementation of uinput is that we just succeed with true when we have validated everything. The only effect of is now that Data is instantiated with whatever the user entered.
If we give a dummy implementation of the actual implementation, we can already try the implementation oursevles:
pure_predicate(_Data, Result) :-
% here goes the actual stuff
Result='we have found something awesome'.
On the prompt we can use the pure predicate without trouble:
?- pure_predicate(someinputdata,Y).
Y = 'we have computed something awesome'.
On the other hand, we can also use the full predicate as follows:
?- main(_).
Airport? wdc.
Day? |: 1.
Month? |: 2.
Year? |: 2000.
we have found something awesome
true.
Since we are using read, we have to input prolog terms and terminate with a dot ., but everything worked as expected.
In case the user input fails, we get:
?- main(_).
Airport? bla(X).
ERROR: Unhandled exception: failure('unknown airport')
Please note that we only went through this trouble to actually fail early and give a user message in that case. For the actual computation, this is completely unneccessary.
In the code below you are making false assumption that T will remain unbound in case if airport will not be found in database:
airport(Air, T)
What happens actually is that call to airport(Air, T) will make isAirport(Air) to fail immediately and your var(T) and other parts will not be executed at all.
Try this code instead:
isAirport(Air) :-
airport(Air, _T), ! ; write('Airport not found'), fail.
I defined my knowledge base as:
edge(mammal,isa,animal).
edge(human,isa,mammal).
edge(simba,isa,human).
edge(animal,swim,bybirth).
edge(human,swim,mustlearn).
path(X,Y) :- edge(X,isa,Y).
path(X,Y) :- edge(X,isa,Z), path(Z,Y).
swim(X,Y) :- edge(X,swim,Y).
swim(X,Y) :- path(X,Z), swim(Z,Y).
Now, to use the above knowledge base, I use the following:
?- swim(simba,bybirth).
?- swim(simba,mustlearn).
And for both the queries, Prolog returns true. I want Prolog to check for the property swim locally first, then look at the direct parent, and so on in a hierarchical fashion. And it should stop searching as soon as we know that Simba "mustlearn" to swim, and shouldn't look any further. Thus, it should return false for the first query and true for the second.
I know it has to be done by limiting backtracking. I tried using the cut and not operators, but couldn't succeed. Is there a way to achieve this?
I tried it and ran into a problem too. I thought this might work:
swim(X,Y) :- once((edge(X,swim,Y); path(X,Z), swim(Z,Y))).
It doesn't work, because if Y is already instantiated on the way in, the first step will fail to unify and it will try the second route going through the human intermediate. So even though the query only produces one result, it can be fooled into producing swim(simba, bybirth). The solution is to force Prolog to commit to a binding on another variable and then check that binding after the commitment:
swim(X,Y) :-
once((edge(X,swim,Method); path(X,Z), swim(Z,Method))),
Method = Y.
This tells Prolog, there is only one way to get to this method, so find that method, and then it must be Y. If you find the wrong method, it won't go on a search, it will just fail. Try it!
I've been struggling trying to edit Eliza chatbot in Prolog. every time I try to edit something, a new error show up. Is it protected to any sort of editing?
I edited using SWI-prolog editor. The problem is that I'm trying to minimize the code without fully understanding it. I'm trying to do a simple short version of it. So, I might removed something essential perhaps! like "my_char_type" for example. The error I got is " retract/1: No permission to modify static procedure `rules/1'"
Is there any code for a smaller chatbot that I can understand?
Please help :'(
Prolog has a static store and a dynamic store. If you open up a file, say program.pl and you put some lines in it like this:
foo(tabitha).
foo(darlene).
those facts wind up in the static store. They're not a mutable part of your program (by default).
The asserta/1, assertz/1 and retract/1 and retractall/1 procedures form the basis of the dynamic store. If you are just sitting at the console you could just add some facts to the dynamic store and remove them by doing something like this:
?- asserta(baz(tabitha)).
true.
?- baz(X).
X = tabitha.
?- retract(baz(tabitha)).
true.
?- baz(X).
false.
However, if you are sitting at the prompt after loading program.pl and you try to retract foo(tabitha) you're going to get the static procedure message:
?- retract(foo(tabitha)).
ERROR: retract/1: No permission to modify static procedure `foo/1'
ERROR: Defined at /Users/fusion/program.pl:1
The reason is because the foo/1 facts were placed in the static store rather than the dynamic store, because you didn't put them there with asserta/1 or assertz/1 or declare the predicate as dynamic, like this:
:- dynamic foo/1.
So there are two ways forward:
Edit the program source directly and reload it.
Declare the rules/1 predicate dynamic as above.
Incidentally, reloading in SWI-Prolog is best done by running make. from the prompt.
I would recommend option #1 since otherwise it will be difficult to reconstruct your working program's state when you like what it is doing.
SWISH has the simplest Eliza ever, I have the old code below, used to test my Prolog interpreter.
here is an example session
1 ?- eliza.
? i am hungry
how long have you been hungry ?
? very long
please go on
? bye
Goodbye. I hope I have helped you
true.
SWI-Prolog tested version, ported from below ELIZA.IL (alas, SWISH is apparently missing IO primitive like read_line_from_codes, so it's simpler to paste the full code)
eliza :-
write('? '), read_word_list(Input), eliza(Input), !.
eliza([bye]) :-
write('Goodbye. I hope I have helped you'), nl.
eliza(Input) :-
pattern(Stimulus, Response),
match(Stimulus, Dictionary, Input),
match(Response, Dictionary, Output),
reply(Output),
!, eliza.
match([N|Pattern], Dictionary, Target) :-
integer(N), lookup(N, Dictionary, LeftTarget),
append(LeftTarget, RightTarget, Target),
match(Pattern, Dictionary, RightTarget).
match([Word | Pattern], Dictionary, [Word | Target]) :-
atom(Word), match(Pattern, Dictionary, Target).
match([], _Dictionary, []).
pattern([i,am,1],[how,long,have,you,been,1,'?']).
pattern([1,you,2,me],[what,makes,you,think,i,2,you,'?']).
pattern([i,like,1],[does,anyone,else,in,your,family,like,1,'?']).
pattern([i,feel,1],[do,you,often,feel,that,way,'?']).
pattern([1,X,2],[can,you,tell,me,more,about,your,X,'?']) :- important(X).
pattern([1],[please,go,on]).
important(father).
important(mother).
important(son).
important(sister).
important(brother).
important(daughter).
reply([Head | Tail]) :-
write(Head), write(' '), reply(Tail).
reply([]) :- nl.
lookup(Key, [(Key, Value) | _Dict], Value).
lookup(Key, [(Key1, _Val1) | Dictionary], Value) :-
Key \= Key1, lookup(Key, Dictionary, Value).
read_word_list(Ws) :-
read_line_to_codes(user_input, Cs),
atom_codes(A, Cs),
tokenize_atom(A, Ws).
Older code: eliza and rwl
I have a simple Prolog-program that I need some help debugging.
The point is to extend the program by pattern-matching to create a proof checker for propositional logic. The problem I have is that I get no when I expect yes and my 'fix' (providing a base case for valid_proof_aux) still gives me two solutions and I don't know why.
Not sure how to go about debugging Prolog yet, sorry.
%call:
valid_proof([p],p,[[1, p, premise]])
%src:
reverse_it([],Z,Z).
reverse_it([H|T],Z,Acc) :- reverse_it(T,Z,[H|Acc]).
valid_proof(Prems,Goal,Proof):-
last(Proof, [_, Goal, _]),
reverse_it(Proof, RevP, []),
valid_proof_aux(Prems, RevP) .
valid_proof_aux(Prems,
[[_,Prop,premise] | T]):-
memberchk(Prop,Prems),
valid_proof_aux(Prems,T).
%my 'fix'
valid_proof_aux(_, []) :- true .
You don't really show how to run the program and what exactly you get (you should edit your question with and add this), so this answer is a bit of a guess, but anyway:
You need the base case either way (as you observe yourself), valid_proof_aux/2 would fail when the list becomes empty [] and does not match [[...]|T] anymore.
?- [] = [_|_]. % try to unify an empty list with a non-empty list
false.
What you need to do to get rid of the choice point is to put the list argument as the first argument.
valid_proof_aux([], _).
valid_proof_aux([[_,Prop,premise]|T], Prems) :-
memberchk(Prop, Prems),
valid_proof_aux(T, Prems).
Note that you don't need the :- true., this is implicit. Also, avoid leaving any blanks on the two sides of the | in [Head|Tail].
I want to write a program that sets cold to true if the user enters winter and sets warm to true if the user enters summer. This is what I have so far:
start :- write('What season is it?: '), read(X), season(X).
cold :- season(winter).
warm :- season(summer).
However when I query start and enter winter for the season I get an error that season/1 is undefined. To fix this I tried changing my code to the following:
start :- season(X).
season(X) :- write('What season is it?: '), read(X).
cold :- season(winter).
warm :- season(summer).
Now when I query start it asks for the season as expected and I entered winter. I queried cold expecting a return of true since I figured season(winter) would be true, but instead I was again prompted to answer "what season is it?". How can I make this simple program work?
It looks that what you want to do is kind of "forward chaining". So you
want to be able to add a fact to the knowledge base, and then see
additional facts pop up.
Such things were very fashionable in the 80's when expert systems emerged
and can also be done with Prolog. Here is an article roughly describing the
difference between "backward chaining" and "forward chaining":
Logic Programming Associates Ltd.
ProWeb: Expert System
I have tried to remodel something similar to the above forward chaining
via the clause expansion mechanism that is available in many Prolog
systems and via an agenda that is held in the knowledge base as ordinary
facts. The expansion mechanism changes a rule of the following form:
P :- A
Into a rule where X is a fresh variable not occuring in P or A:
delta(X, P) :- A_new(X)
Where A_new is a condition that says that P is new when a new fact
X has arrived. Lets consider your example:
?- ['delta.p'].
?- [user].
:- forward season/1.
:- forward cold/0.
:- forward warm/0.
cold :- season(winter).
warm :- season(summer).
^D
These two Prolog rules would be turned into the following delta/2 rules
via the rewriting. Since the rules are very simple, the delta/2 rules
are also very simple:
?- listing(delta/2).
delta(X, cold) :-
X = season(winter).
delta(X, warm) :-
X = season(summer).
Here is an example session:
?- list.
Yes
?- volunteer(season(winter)).
Yes
?- list.
season(winter).
cold.
Yes
The rewriting in delta.p is very primitive. The rewriting can be enhanced
to support rigid predicates and to support the dynamic removal of facts.
Possible additional application areas of the "forward chaining" are then:
Natural Language Processing
Constraint Solving
Best Regards
The current delta/2 rewriting after a few more years development:
http://www.jekejeke.ch/idatab/doclet/blog/en/docs/15_min/02_reference/minimal/delta.html
An Article showing that a Constraint Store can be modelled:
https://plus.google.com/+JekejekeCh/posts/8oHErwopKxK
P.S.: The predicate name volunteer/1 stems form Nexpert Object,
an expert system shell in use in the 80's and 90's.
NEXPERT OBJECT version 3.0, Jean-Marie Chauvet, Neuron Data, Inc.
P.P.S.: But you might want to lookup newer things, such as Rete-NT, or OWL:
http://answers.semanticweb.com/questions/3304/forward-vs-backward-chaining
Because "cold/0" and "warm/0" take no arguments, it is impossible to change the result at runtime without altering their definition at runtime. The simplest way to do this is just to add or remove the facts "cold." and "warm." depending on user input, using assert/1 and retractall/1.
start :- write('What season is it?: '), read(X), season(X).
season(summer) :- retractall(cold), assert(warm).
season(winter) :- retractall(warm), assert(cold).
While defining the facts, you need to change the syntax a little bit.
start(Y) :- write('What season is it?: '), read(X), nl, season(X,Y).
season(winter,cold).
season(summer,warm).
Note that the user needs to end the input with a full stop. (eg. winter.)
season(winter,cold).
season(summer,warm).
start:-write('What season is it?'),write(' '),read(X),season(X,Y),write(Y).
The output will be look like this:
?- start.
What season is it? winter.
cold
true.