Prolog programming is all about writing knowledge bases (KB), collection of facts and rules. The basic formatting is as follows:
.
at the end
The basic format for a fact is predicate(arguments).
Example 1: Use the following predicates to describe the facts:
1eecs(X): X is an EECS course.
2math(X): X is an MATH course.
To describe eecs280
, eecs281
, eecs445
, eecs492
and math214
, we could write:
xxxxxxxxxx
51eecs(eecs280).
2eecs(eecs281).
3eecs(eecs245).
4eecs(eecs292).
5math(math214).
The predicate may take more than 1 argument.
Example 2: Use the following predicates to describe the facts:
xxxxxxxxxx
31prerequisite(X, Y): X is a prerequisite of Y.
2taken(X, Y): X has taken course Y.
3teach(X, Y): X teaches course Y.
Given that martin
has taken eecs445
, and teaches eecs280
and eecs492
, we could write:
x1prerequisite(eecs280, eecs281).
2prerequisite(eecs203, eecs281).
3prerequisite(eecs281, eecs492).
4prerequisite(eecs281, eecs445).
5prerequisite(math214, eecs445).
6
7teach(martin, eecs492).
8teach(martin, eecs280).
9taken(martin, eecs445).
The basic format for a fact is predicate(arguments) :- predicate(arguments).
The :-
should be read as “if”, or “is implied by”.
Example 1: Use the following predicates to describe the facts:
xxxxxxxxxx
11teach(X, Y) => taken(X, Y): If X teaches Y, X must have taken Y.
To describe this rule, we could write:
xxxxxxxxxx
11taken(X, Y) :- teach(X, Y).
Store your KB in a .pl
file, open it in the SWI-prolog, you should see something similar to this:
xxxxxxxxxx
81Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.22-114-g7e1bb6838)
2SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
3Please run ?- license. for legal details.
4
5For online help and background, visit https://www.swi-prolog.org
6For built-in help, use ?- help(Topic). or ?- apropos(Word).
7
8?-
To question the KB if a fact is true, one make a query after ?-
.
Example 1: Is math214
an eecs
course?
xxxxxxxxxx
21?- eecs(math214).
2false.
Example 2: Is math214
a prerequisite of eecs492
?
xxxxxxxxxx
21?- prerequisite(math214, eecs492).
2false.
Example 3: Have martin
taken eecs492
?
xxxxxxxxxx
21?- taken(martin, eecs492).
2true.
Note that although taken(martin, eecs492)
is not directly stated in the KB, prolog makes the logical inference from the fact that teach(martin, eecs492)
and taken(X, Y) :- teach(X, Y)
. In other words, since Martin is teaching EECS 492, he must have taken this course earlier.
Example 4: List all courses that martin
has taken?
xxxxxxxxxx
51?- taken(martin, X), print(X), nl, fail.
2eecs445
3eecs492
4eecs280
5false.
You may have noticed that in the earlier example, the logic is not complete. If one has taken one course, he/she must have taken the prerequisites.
One may write a new rule like:
xxxxxxxxxx
11taken(X, Y) :- taken(X, Z), prerequisite(Y, Z).
It seems fine when you make the query:
xxxxxxxxxx
21?- taken(martin, eecs281).
2true .
However, the problem arises when you try to check all course that Martin has taken:
xxxxxxxxxx
121?- taken(martin, X), print(X), nl, fail.
2eecs445
3eecs492
4eecs280
5eecs281
6math214
7eecs281
8eecs280
9eecs203
10eecs280
11eecs203
12
Oops! Looks like your program gets stuck in an infinite loop! Prolog applies backward chaining and depth-first search to do logic inference, and this will lead to an infinite search tree. Check the Recursion section for details.
To stop Prolog from an infinite loop, press ctrl + C
, and press A
to abort.
xxxxxxxxxx
131?- taken(martin, X), print(X), nl, fail.
2eecs445
3eecs492
4eecs280
5eecs281
6math214
7eecs281
8eecs280
9eecs203
10eecs280
11eecs203
12Action (h for help) ? abort
13% Execution Aborted
A better way is to avoid recursion in Prolog. Use a new predicate instead.
xxxxxxxxxx
21know(X, Y) :- taken(X, Y).
2know(X, Y) :- taken(X, Z), prerequisite(Y, Z).
And the query works fine here:
xxxxxxxxxx
81?- know(martin, X), print(X), nl, fail.
2eecs281
3math214
4eecs281
5eecs445
6eecs492
7eecs280
8false.
CFG is a finite collection of rules indicating that...
CFG captures constituents and ordering.
CFG consists of...
Set of terminals (words);
Set of non-terminal (the constituent of language);
s
, np
, vp
, det
, n
, v
.Sets of rules of the form A -> , where is a string of zero or more terminals and non-terminals.
An exemplar CFG is like:
xxxxxxxxxx
121s -> np vp
2np -> n
3np -> det n
4vp -> v
5vp -> v np
6
7det -> a
8det -> the
9n -> student
10n -> prolog
11v -> likes
12v -> hates
Consider a sentence (a string of words): the student likes prolog
. The question is, is this grammatical according to our little grammar? And if it is, what structure does it have?
A derivation is a sequence of rules applied to a string that accounts for that string:
It can be represented in:
[s [np [det the] [n student]] [vp [v likes] [np [n prolog]]]]
In tree form, it looks like:
xxxxxxxxxx
91s
2________|________
3np vp
4___|___ ____|____
5det n v np
6| | | |
7the student likes n
8|
9prolog
DCG is a notation for writing grammar that hides the underlying difference list variables.
Translate the previous CFG into DCG is straightforward:
xxxxxxxxxx
121s --> np, vp.
2np --> n.
3np --> det, n.
4vp --> v.
5vp --> v, np.
6
7det --> [a].
8det --> [the].
9n --> [student].
10n --> [prolog].
11v --> [likes].
12v --> [hates].
Example 1: find out whether the student likes prolog
is a sentence.
xxxxxxxxxx
21?- s([the, student, likes, prolog],[]).
2true .
Example 2: find out if the student
is a noun phrase (np
).
xxxxxxxxxx
21?- np([the, student],[]).
2true.
Example 3: generate all the sentences in the grammar.
xxxxxxxxxx
91?- s(X,[]), print(X), nl, fail.
2[student,likes]
3[student,hates]
4[student,likes,student]
5[student,likes,prolog]
6...
7[the,prolog,hates,the,student]
8[the,prolog,hates,the,prolog]
9false.
How to locate the *.pl
file in prolog?
Suppose you have a directory eecs492/prolog
and all your prolog files are under this directory. You can go to this directory first, and then start Prolog /user/eecs492/xsb
. Once you are in the Prolog environment, you can load the file by typing [filename]
.
When I load up my file, I receive some warnings about singleton variables. Does that matter?
The reason is that you have variables defined in your predicates that have not been referenced or used in the body. You can replace those variables with an anonymous variable _
(underscore).
For example, if you define the following "member" predicate, "Y" is not used, so you will have a "singleton" warning.
xxxxxxxxxx
21member(X,[X|Y].
2member(X,[Y|T]) :- member(X,T)
If you define it as the following, you should be fine.
xxxxxxxxxx
21member(X,[X|]).
2member(X,[|T]) :- member(X,T).
I have a problem with duplicate answers, is that ok?
The reason for this to happen is that you have multiple facts/rules that are matched by the prolog interpreter. Since the tutorial didn't mention how to prevent that, it is ok for your program to produce duplicate results. (no points will be taken from you in this regard). One way to fix it is to use the built-in “setoff”.
So you can make the following changes to avoid duplicates.
xxxxxxxxxx
21brother0(X,Y) :- ....
2brother(X,Y) :- setof(_, brother0(X,Y), _).
I'm having trouble figuring out how to prevent a "reflexive" type of relationship from happening. For example, Harry should not be Harry's brother and the same for his sister.
To avoid this, you will need to specify that X and Y cannot be the same as in the following:
xxxxxxxxxx
11sister(X, Y) :- child(X, Z), child(Y, Z), female(X), X \== Y.
For a "taken" response should "on" be changed to "off"? For example, my current code returns the following. Should it respond with "I have taken the cone off the square."?
xxxxxxxxxx
31?- command([take,the,cone,on,the,square]).
2I have taken the cone on the square.
3yes
No you don't need to change "on" to "off" in the "take" case. Basically, "on ..." is modifying the thing that should be taken. In your example, "on the square" is modifying "the cone".
How do I print out the response?
One way of doing it is to take the input list (e.g., [put, the, red, block, on, the, green, circle]
), and convert it to another output list. To come up with this output list, you need to check each member of the original list to see whether it requires any change. Once you have the output list, you can simply write each member of the output list out using "write". The key here is to do some list operations.
When implementing the predicate "command", this command will take an argument and do some processing, then provide output.
An example could be:
xxxxxxxxxx
21command(X) :- X==[],!, write(' it is an empty list').
2command(X) :- write('it is not an empty list").
Then you can test it:
xxxxxxxxxx
61?- command([]).
2this is an empty list
3yes
4?- command([a,b,c]).
5this is not an empty list
6yes
Certainly the above is only an example, you will need to re-implement “command” to fit that to our problem here.
I have np
defined as follows. The problem is that the last definition of np
never halts.
xxxxxxxxxx
31np --> det, adj, n.
2np --> det, n.
3np --> np, pp.
Your third rule np-->np, pp
will loop infinitely. You can change np-->np, pp
to "np-->det, n, pp
, etc.
Is it alright to have an infinite number of chains for the prepositional section of the sentence? For example, is [put,the,cone,on,the,cone,on,the,cone,on,the,cone,on,the,cone....]
valid input, or should this terminate after 1 or 2 prepositions?
It should be able to support an arbitrary number of pp
s as valid input. But all the testing cases will not go beyond the ones you've seen in the training data.
A valid sentence is take the ball
, but it looks like put the ball
is not. I'm not sure how to accept take
but not put
for my one rule that accepts a noun with an empty pp
.
Good catch here! There is indeed a difference between “put” and “take”. We have not talked about lexicalized grammar, so we will not worry about this difference for now. My testing cases will be strictly following the training examples, will not be testing anything like “put that ball”.
However, for your information, to distinguish “put” and “take” in this use, we can have lexicalized grammar. Basically, instead of a general “V”, we can separate it to “V-Put” and “V-Take”. Then other grammar rules will be updated accordingly to use “V-Put” and “V-Take”.
Are cubes, cones, and blocks the only nouns that can have action taken against them? (e.g., you cannot take a square or circle)
You made a very good observation. To make the problem simpler, here we mainly focus on the syntax, without worrying about the semantics, e.g., what objects can or cannot be taken. So all the nouns should behave the same even though we do not see "take the square" in the example.