Left corner parser in prolog for simple sentence - prolog

I am trying to build a top left parser in prolog for the sentence "The little pigs were safe inside" and I am trying to figure out where I went wrong in my code. (Note: I'm a beginner in prolog and the code I wrote is based on a working example I had)
Can anybody point me in the right direction on this and maybe shine some light on it?
Code:
% The little pigs were safe inside.
% ------------------------------------------------ TERMINALS ------------------------------------------------
leaf(determiner) --> [the].
leaf(adjective) --> [little].
leaf(adjective) --> [safe].
leaf(noun) --> [pigs].
leaf(verb) --> [were].
leaf(adverb) --> [inside].
% ------------------------------------------------ NON-TERMINALS ------------------------------------------------
lcd(noun_phrase,sentence):-!.
lcd(determiner,noun_phrase):-!.
lcd(adjective,adjective_phrase):-!.
lcd(adverb,adjective_phrase):-!.
lcd(verb,verb_phrase):-!.
lc(X,X):-!.
lc(X,Y):-lcd(X,Y),!.
lc(X,Y):-lcd(X,Z),lc(Z,Y).
% ------------------------------------------------ RULES ------------------------------------------------
parse(Nterm,As,[W0|W1],Wn):-
leaf(Pterm,[W0|W1],W1),
lc(Pterm,Nterm),
Ap=..[Pterm,W0],
P=..[Pterm,Nterm,Ap,As,W1,Wn],
call(P).
noun_phrase(Nt,NP,As)-->{lc(sentence,Nt)},parse(verb_phrase, VP),sentence(Nt,sentence(NP,VP),As),!.
noun_phrase(noun_phrase,E,E)-->[].
determiner(Nt,D,As)-->{lc(noun_phrase,Nt)},parse(adjective_phrase,N),noun_phrase(Nt,noun_phrase(D,N),As).
adjective(Nt,A,As)-->{lc(adjective_phrase,Nt)},parse(adjective_phrase,N),adjective_phrase(Nt,adjective_phrase(A,N),As).
adverb(Nt,A,As)-->{lc(adjective_phrase,Nt)},parse(adjective_phrase,N),adjective_phrase(Nt,adjective_phrase(A,N),As).
noun(Nt,N,As)-->{lc(adjective_phrase,Nt)},adjective_phrase(Nt,adjective_phrase(N),As).
adjective_phrase(adjective_phrase,A,A)-->[].
verb(Nt,V,As)-->{lc(verb_phrase,Nt)},parse(noun_phrase,N),verb_phrase(Nt,verb_phrase(V,N),As).
verb(Nt,V,As)-->{lc(verb_phrase,Nt)},verb_phrase(Nt,verb_phrase(V),As).
verb_phrase(verb_phrase,A,A)-->[].
sentence(s,A,A)-->[].
test(S,A):-parse(S,A,[the, little, pigs, were, safe, inside],[]).
Test trace:
trace,test(S,A).
Call: (9) test(_4648, _4650) ? creep
Call: (10) parse(s, _4650, [the, little, pigs, were, safe, inside], []) ? creep
Call: (11) leaf(_5024, [the, little, pigs, were, safe, inside], [little, pigs, were, safe, inside]) ? creep
Exit: (11) leaf(determiner, [the, little, pigs, were, safe, inside], [little, pigs, were, safe, inside]) ? creep
Call: (11) lc(determiner, s) ? creep
Call: (12) lcd(determiner, s) ? creep
Fail: (12) lcd(determiner, s) ? creep
Redo: (11) lc(determiner, s) ? creep
Call: (12) lcd(determiner, _5026) ? creep
Exit: (12) lcd(determiner, noun_phrase) ? creep
Call: (12) lc(noun_phrase, s) ? creep
Call: (13) lcd(noun_phrase, s) ? creep
Fail: (13) lcd(noun_phrase, s) ? creep
Redo: (12) lc(noun_phrase, s) ? creep
Call: (13) lcd(noun_phrase, _5026) ? creep
Exit: (13) lcd(noun_phrase, sentence) ? creep
Call: (13) lc(sentence, s) ? creep
Call: (14) lcd(sentence, s) ? creep
Fail: (14) lcd(sentence, s) ? creep
Redo: (13) lc(sentence, s) ? creep
Call: (14) lcd(sentence, _5026) ? creep
Fail: (14) lcd(sentence, _5026) ? creep
Fail: (13) lc(sentence, s) ? creep
Fail: (12) lc(noun_phrase, s) ? creep
Fail: (11) lc(determiner, s) ? creep
Redo: (11) leaf(_5024, [the, little, pigs, were, safe, inside], [little, pigs, were, safe, inside]) ? creep
Fail: (11) leaf(_5024, [the, little, pigs, were, safe, inside], [little, pigs, were, safe, inside]) ? creep
Fail: (10) parse(s, _4650, [the, little, pigs, were, safe, inside], []) ? creep
Fail: (9) test(_4648, _4650) ? creep
false.
The expected output for this grammar when running the test should be something like:
S = [the, little, pigs, were, safe, inside],A = s(np(d(the), adjp(adj(little), n(pigs))), vp(v(were), adjp(adj(safe), adv(inside)))) .

For anyone who might need this later on.
Only a few fixes were needed to make this work. Compare the two programs to find the mistakes :)
Solution
% The little pigs were safe inside.
% ------------------------------------------------ TERMINALS ------------------------------------------------
leaf(determiner) --> [the].
leaf(adjective) --> [little].
leaf(adjective) --> [safe].
leaf(noun) --> [pigs].
leaf(verb) --> [were].
leaf(adverb) --> [inside].
% ------------------------------------------------ NON-TERMINALS ------------------------------------------------
lcd(noun_phrase,sentence):-!.
lcd(determiner,noun_phrase):-!.
lcd(verb,verb_phrase):-!.
lcd(adjective,adjective_phrase):-!.
lc(X,X):-!.
lc(X,Y):-lcd(X,Y),!.
lc(X,Y):-lcd(X,Z),lc(Z,Y).
parse(Nterm,As,[W0|W1],Wn):-
leaf(Pterm,[W0|W1],W1),
lc(Pterm,Nterm),
Ap=..[Pterm,W0],
P=..[Pterm,Nterm,Ap,As,W1,Wn],
call(P).
% ------------------------------------------------ RULES ------------------------------------------------
noun_phrase(Nt,NP,As)-->{lc(sentence,Nt)},parse(verb_phrase, VP),sentence(Nt,sentence(NP,VP),As),!.
noun_phrase(noun_phrase,E,E)-->[].
determiner(Nt,D,As)-->{lc(noun_phrase,Nt)},parse(adjective_phrase,N),noun_phrase(Nt,noun_phrase(D,N),As).
adjective_phrase(adjective_phrase,A,A)-->[].
verb(Nt,V,As)-->{lc(verb_phrase,Nt)},parse(adjective_phrase,N),verb_phrase(Nt,verb_phrase(V,N),As).
verb_phrase(verb_phrase,A,A)-->[].
adjective(Nt,A,As)-->{lc(adjective_phrase,Nt)},parse(noun,N),adjective_phrase(Nt,adjective_phrase(A,N),As).
noun(noun,A,A)-->[].
adjective(Nt,A,As)-->{lc(adjective_phrase,Nt)},parse(adverb,Adv),adjective_phrase(Nt,adjective_phrase(A,Adv),As).
adverb(adverb,A,A)-->[].
sentence(sentence,A,A)-->[].
% ------------------------------------------------ TEST ------------------------------------------------
test(sentence,A):-parse(sentence,A,[the, little, pigs, were, safe, inside],[]).
test1(sentence,A):-parse(sentence,A,[the, little, pigs],[]).

Related

prolog questions.. fails to invoke predicate

I am trying to understand why duck2 is not called.. Should I cover all cases of the two list being empty, non empty.
duck([H|T], something ) :-
write("*"),
write("?"),
duck2([H|T], T, something_else),
write("Over").
duck2([], [], something_else) :- write("AllDone").
duck2([H|T], [H1,T1], something_else) :-
write("I am here").
trace gives this..
Call: (16) duck([dog, eats], [eats], something) ? creep
Call: (17) write("*") ? creep
*
Exit: (17) write("*") ? creep
Call: (17) write("?") ? creep
?
Exit: (17) write("?") ? creep
Call: (17) duck2([dog, eats], [eats], something_else) ? creep
Fail: (17) duck2([dog, eats], [eats], something_else) ? creep
Fail: (16) duck([dog, eats], [eats], something) ? creep
duck2([H|T], [H1,T1], something_else) :-
% spaced out to line up with the below trace
duck2([H|T], [H1,T1], something_else) :-
Call: (17) `duck2([dog, eats], [eats], something_else)` ? creep
[H1, T1] is a list of two items and [eats] is a list of one item; if you try and unify them, they don't unify:
?- [H1, T1] = [eats].
false
so the call fails.
Should I cover all cases
Well, at least cover enough cases to make the code work. All cases is up to you.

Prolog goal with multiple results

spec(comp1, pc, 32). /* Fact 1 */
spec(comp2, mac, 128). /* Fact 2 */
spec(comp3, pc, 64). /* Fact 3 */
runs(pc, movie_edit, 96). /* Fact 4 */
runs(pc, vb, 16). /* Fact 5 */
runs(pc, cpp, 28). /* Fact 6 */
runs(mac, vb, 24). /* Fact 7 */
runs(mac, prolog, 128). /* Fact 8 */
access(judy, comp1). /* Fact 9 */
access(peter, comp3). /* Fact 10 */
access(david, comp1). /* Fact 11 */
access(david, comp2). /* Fact 12 */
can_use(P, SW) :- access(P, Comp), can_run(Comp, SW). /* Rule 1 */
can_run(Comp, SW) :- spec(Comp, CompType, MemAvail),
runs(CompType, SW, MemNeeded),
MemAvail >= MemNeeded. /* Rule 2 */
?- can_use(judy, vb).
?- can_use(david, prolog).
The first goal returns: true, false.
Whereas the second one returns only true.
My question is why in the first goal we have that extra information.
I'm using SWI-Prolog 7.6.4 version
The reason this happens is because in the former case, there is still "opportunity" for backtracking whereas in the latter, there is no such opportunity.
If we call the goal with the trace, we see:
[trace] ?- can_use(judy, vb).
Call: (8) can_use(judy, vb) ? creep
Call: (9) access(judy, _2968) ? creep
Exit: (9) access(judy, comp1) ? creep
Call: (9) can_run(comp1, vb) ? creep
Call: (10) spec(comp1, _2968, _2970) ? creep
Exit: (10) spec(comp1, pc, 32) ? creep
Call: (10) runs(pc, vb, _2970) ? creep
Exit: (10) runs(pc, vb, 16) ? creep
Call: (10) 32>=16 ? creep
Exit: (10) 32>=16 ? creep
Exit: (9) can_run(comp1, vb) ? creep
Exit: (8) can_use(judy, vb) ? creep
true ;
Redo: (10) runs(pc, vb, _2970) ?
We here thus make a call to runs/3 with runs(pc, vb, MemNeeded) and Prolog finds a first answer with 16. But it sets a backtracking point to look for other runs/3 facts with runs(pc, vb, MemNeeded). Imagine that there is another fact later in the source code, for example runs(pc, vb, 14) at the end, then this can produce another answer.
If we however call the second goal, we see:
[trace] ?- can_use(david, prolog).
Call: (8) can_use(david, prolog) ? creep
Call: (9) access(david, _3726) ? creep
Exit: (9) access(david, comp1) ? creep
Call: (9) can_run(comp1, prolog) ? creep
Call: (10) spec(comp1, _3726, _3728) ? creep
Exit: (10) spec(comp1, pc, 32) ? creep
Call: (10) runs(pc, prolog, _3728) ? creep
Fail: (10) runs(pc, prolog, _3728) ? creep
Fail: (9) can_run(comp1, prolog) ? creep
Redo: (9) access(david, _3726) ? creep
Exit: (9) access(david, comp2) ? creep
Call: (9) can_run(comp2, prolog) ? creep
Call: (10) spec(comp2, _3726, _3728) ? creep
Exit: (10) spec(comp2, mac, 128) ? creep
Call: (10) runs(mac, prolog, _3728) ? creep
Exit: (10) runs(mac, prolog, 128) ? creep
Call: (10) 128>=128 ? creep
Exit: (10) 128>=128 ? creep
Exit: (9) can_run(comp2, prolog) ? creep
Exit: (8) can_use(david, prolog) ? creep
true.
Here we call runs(mac, prolog, MemNeeded)., and this is the last fact of runs/3, hence there is no other possibility to satisfy runs/3 otherwise: since Prolog runs top to bottom, if we have satisfied the last fact/clause, we know that there is no other option.
Since all other calls also take the last predicate as well, or with a different constant as first parameter (SWI-Prolog looks at the first argument when it compiles the source code as an optimization), there are no other backtracking points, and thus there is no way to Redo a certain call.

Brother in law predicate for Prolog not working

Here's the relevant code:
married(X,Y) :- wife(X,Y);husband(X,Y).
parent(X,Y) :- father(X,Y) ;mother(X,Y).
brother(X,Y) :-
man(X),
parent(Z,X),
parent(Z,Y),
X \= Y.
brother_in_law(X,Y) :-
brother(X,Z),married(Z,Y).
I've googled and it seems other have been using the exact code for the brother in law predicate so it should be fine? I checked the other predicates and they seem good too.. not sure what is happening.
Also, by not working I mean that it's not acknowledging the relevant relation when I check.
Look at the trace and you will see the problem:
?- trace, brother_in_law(prins-daniel, Y).
Call: (9) brother_in_law(prins-daniel, _11346) ? creep
Call: (10) brother(prins-daniel, _11680) ? creep
Call: (11) man(prins-daniel) ? creep
Exit: (11) man(prins-daniel) ? creep
Call: (11) parent(_11678, prins-daniel) ? creep
Call: (12) father(_11678, prins-daniel) ? creep
Fail: (12) father(_11678, prins-daniel) ? creep
Redo: (11) parent(_11678, prins-daniel) ? creep
Call: (12) mother(_11678, prins-daniel) ? creep
Fail: (12) mother(_11678, prins-daniel) ? creep
Fail: (11) parent(_11678, prins-daniel) ? creep
Redo: (11) man(prins-daniel) ? creep
Fail: (11) man(prins-daniel) ? creep
Fail: (10) brother(prins-daniel, _11680) ? creep
Fail: (9) brother_in_law(prins-daniel, _11346) ? creep
false.
Who is prins-daniel's father? You don't have a fact for that. Who is prins-daniel's mother? You don't have a fact for that either. As a result, you can't find any brothers, so the query fails.
Does this mean you are missing facts or missing code? The code says X and Y are brothers-in-law if X has a brother Z who is married to Y. Is this the only way to have a brother-in-law?
Side note: prins-daniel is not an atom in Prolog as it would be in Lisp. This is a term:
?- write_canonical(prins-daniel).
-(prins,daniel)
The situation is compounded by longer terms:
?- write_canonical(johann-georg-av-hohenzollern).
-(-(-(johann,georg),av),hohenzollern)
Just something to be aware of.

Prolog checking the same fact twice

This is the knowledge database
student(jack,100,21,m).
takes(100,cs01).
takes(100,cs02).
takes(100,cs03).
teaches(doe,cs01).
course(cs01,ai).
course(cs02,cpp).
course(cs03,java).
isTaughtBy(Sname,Lname) :-
teaches(Lname,Mcode),
student(Sname,Scode,_,_),
takes(Scode,Mcode).
When running isTaughtBy(jack,doe) it returns true (like its supposed to) and false (for some reason, probably because jack takes multiple modules).
This is the trace:
[trace] ?- isTaughtBy(jack,doe).
Call: (8) isTaughtBy(jack, doe) ? creep
Call: (9) teaches(doe, _18526) ? creep
Exit: (9) teaches(doe, cs01) ? creep
Call: (9) student(jack, _18526, _18528, _18530) ? creep
Exit: (9) student(jack, 100, 21, m) ? creep
Call: (9) takes(100, cs01) ? creep
Exit: (9) takes(100, cs01) ? creep
Exit: (8) isTaughtBy(jack, doe) ? creep
true ;
Redo: (9) takes(100, cs01) ? creep
Fail: (9) takes(100, cs01) ? creep
Fail: (8) isTaughtBy(jack, doe) ? creep
false.
Why is it redoing takes(100,cs01) if it was already checked above? Why is it returning false even though its clearly defined in the database? What am i not understanding here? I just want it to return true or false if a student is taught by a lecturer on any of the modules they are taking.

Some doubts about declarative meaning of a program that prints a list of lists in Prolog

Write a rule that take a list whose elements are themselves lists and
print on each line the elements of internals list:
Example:
?- printList[[1,a],[2,b]]).
1 a
2 b
The solution it the following one:
/* BASE CASE: The list is empty, so there is nothing to print */
printList([]).
printList([P|R]):- printList(P),
!,
nl,
printList(R).
printList([X|R]):- write(X),
!,
printList(R).
I have the printList rule that reaches the base case when the list is empty and in this case there is nothing to print.
If I am not in the base case (the list is not empty) it calls the second rule printList([P|R]) and now I have my first doubt:
The element P is a list because Prolog automatically handles an internal list as an element?
So, If I have something like:
[[1,a],[2,b],[3,c],[4,d]] I have that Prolog automatically match in this way:
P = [1,a] (the first list as the head element)
R = [[2,b],[3,c],[4,d]] (the other 3 list are the element of a tail list)
And then the program calls the printList predicate on the head element (the first list) and this version of the rule writes all the elements in the current list.
Is this right this interpretation?
Now I have some doubts about about the trace of this program, for example if I execute this statement I obtain the following trace:
[trace] ?- printList([[1,a], [2,b]]).
Call: (6) printList([[1, a], [2, b]]) ? creep
Call: (7) printList([1, a]) ? creep
Call: (8) printList(1) ? creep
Fail: (8) printList(1) ? creep
Redo: (7) printList([1, a]) ? creep
Call: (8) write(1) ? creep
1
Exit: (8) write(1) ? creep
Call: (8) printList([a]) ? creep
Call: (9) printList(a) ? creep
Fail: (9) printList(a) ? creep
Redo: (8) printList([a]) ? creep
Call: (9) write(a) ? creep
a
Exit: (9) write(a) ? creep
Call: (9) printList([]) ? creep
Exit: (9) printList([]) ? creep
Exit: (8) printList([a]) ? creep
Exit: (7) printList([1, a]) ? creep
Call: (7) nl ? creep
Exit: (7) nl ? creep
Call: (7) printList([[2, b]]) ? creep
Call: (8) printList([2, b]) ? creep
Call: (9) printList(2) ? creep
Fail: (9) printList(2) ? creep
Redo: (8) printList([2, b]) ? creep
Call: (9) write(2) ? creep
2
Exit: (9) write(2) ? creep
Call: (9) printList([b]) ? creep
Call: (10) printList(b) ? creep
Fail: (10) printList(b) ? creep
Redo: (9) printList([b]) ? creep
Call: (10) write(b) ? creep
b
Exit: (10) write(b) ? creep
Call: (10) printList([]) ? creep
Exit: (10) printList([]) ? creep
Exit: (9) printList([b]) ? creep
Exit: (8) printList([2, b]) ? creep
Call: (8) nl ? creep
Exit: (8) nl ? creep
Call: (8) printList([]) ? creep
Exit: (8) printList([]) ? creep
Exit: (7) printList([[2, b]]) ? creep
Exit: (6) printList([[1, a], [2, b]]) ? creep
true.
This is quite clear for me (I think that my previous reasoning is right) but I am not understanding why whenever it reaches an element in an internal list it calls the printList relation on it (that is a simple element and not a list), for example here:
Call: (8) printList(1) ? creep
Fail: (8) printList(1) ? creep
the program had considered the first list of the original list and then, into it to have to print its first element, why call the printList relation on this simple element
Is it because this simple element may in turn be internal lists?
Something like:
[[[1.1,a1,a2],[1.2,b1,b2]], [2,b]] (in which I have a list that contains 2 lists and the first element it is a list that contains 2 lists. So the program checks if an element it is an element or an internal list?
I think you're over-thinking it. Look at the code:
printList([P|R]):- printList(P),
Right there you can see that printList/1 is being unified with the head of the list. The fact that all the rules of printList/1 match lists and only lists is a fact that you, the human, can immediately see. But Prolog does not "notice" this fact, so if you are to call, say,
printList([1])
the first matching rule is the one above, so it will immediately try to unify printList(1). This will fail, naturally, because 1 is not a list and so doesn't match any of the rules for printList/1. Prolog then backtracks and tries the next rule, which is the one that starts like this:
printList([X|R]):- write(X),
This is clearly going to unify [1] with X = 1, R = [], so it's clearly going to write the first element, the one, and then proceed as usual. There is no magic here involving "internal lists," which as far as I'm aware is not a concept in Prolog at all (if such a thing is treated in the compiler it is well hidden from the user of Prolog).
Prolog isn't psychic; it has to try the rules to see if they fail, even if that attempt is essentially a failing pattern-match in the head of the Horn clause.
I am unable to distinguish your first from your second question so I hope this answers both. :)

Resources