make multiple assert basing on pattern matching - prolog

Given a parse tree of a string my goal is to update my knowledge base.
From a sentence with variable length like the following:
"node 1 is near node 2 that is near node 3 that is near node 4 that..."
in my representation becomes a parse tree representation of the sentence, for example:
s(desc(np(noun(node),id(1)),vp(verb(is),prep(near),np(noun(node),id(2),rel_clause(rel(that)...
from which I'd like to extract and assert the following informations:
edge(1,2),edge(2,3),edge(3,4).
How can I achieve this goal?
I've tried to manage some case with something like
:- dynamic edge/2.
extract(T):- T= s(desc(np(noun(node),id(A)),vp(verb(is),prep(near),np(noun(node),id(B)))),
assert(edge(A,B)).
extract(T):- T= s(desc(np(noun(node),id(A)),vp(verb(is),prep(near),np(noun(node),id(B),rel_clause(rel(that)...
etc
but I'd like to manage potential infinite sentences.
I'm using SWI-prolog.
EDIT:
complete example of a parse tree I get in input:
desc(np(noun(node), id(1)), vp(verb(is), prep(near), np(noun(node), id(2),
rel_clause(rel(that), vp(verb(is), prep(near), np(noun(node), id(3),
rel_clause(rel(that), vp(verb(is), prep(near), np(noun(node), id(4)))))))))

The first thing to do is come up with a more useable description of your data. One way is to break it down like this:
description = desc(subject, verb_part)
subject = np(noun(node), id(A))
verb_part = vp(verb(is), prep(near), object_part)
object_part = np(noun(node), id(B))
object_part = np(noun(node), id(B), rel_part)
rel_part = relcl(rel(that), verb_part)
From here, you can see where the recursion occurs and write predicates which align with the definitions above:
% description = desc(subject, verb_part)
% subject = np(noun(node), id(A))
%
extract(desc(np(noun(node), id(A)), VerbPart)) :-
select_edge(A, VerbPart).
% verb_part = vp(verb(is), prep(near), object_part)
%
select_edge(A, vp(verb(is), prep(near), ObjectPart)) :-
connect_node(A, ObjectPart).
% object_part = np(noun(node), id(B))
%
connect_node(A, np(noun(node), id(B))) :-
assertz(edge(A, B)).
% object_part = np(noun(node), id(B), rel_part)
% rel_part = relcl(rel(that), verb_part)
%
connect_node(A, np(noun(node), id(B), relcl(rel(that), VerbPart))) :-
assertz(edge(A, B)),
select_edge(B, VerbPart).
To execute:
| ?- extract(desc(np(noun(node), id(1)), vp(verb(is), prep(near), np(noun(node), id(2),
relcl(rel(that), vp(verb(is), prep(near), np(noun(node), id(3),
relcl(rel(that), vp(verb(is), prep(near), np(noun(node), id(4))))))))))).
true ? ;
no
The results are asserted, as can be seen if we list the edge/2 facts:
| ?- listing(edge).
% file: user_input
edge(1, 2).
edge(2, 3).
edge(3, 4).
yes
You can also collect the edges in a list instead of asserting them, and have a result of the query be, [edge(1,2), edge(2,3), edge(3,4)]:
extract(desc(np(noun(node), id(A)), VerbPart), Edges) :-
select_edge(A, VerbPart, Edges).
select_edge(A, vp(verb(is), prep(near), ObjectPart), Edges) :-
connect_node(A, ObjectPart, Edges).
connect_node(A, np(noun(node), id(B)), [edge(A,B)]).
connect_node(A, np(noun(node), id(B), relcl(rel(that), VerbPart)), [edge(A,B)|Edges]) :-
select_edge(B, VerbPart, Edges).
And then assert them all at once from the resulting list with maplist:
extract(Description, Edges), maplist(assertz, Edges).

Related

Recursive addition in Prolog with Peano numbers doesn't work

I'm currently trying to practice some Prolog. I just started and I'm facing a problem I don't quite understand. I want to determine recursively if one Peano number is the double of another one. I tried to solve it like this:
isDouble(0, X,X).
isDouble(s(Peano1), Peano1, Peano2) :-
isDouble(Peano1, s(Peano1), Peano2).
For some reason it doesn't work. Does anyone know why?
You don't need three arguments to define such a predicate. Just consider what relation it should describe: a relation between a number and its double. Hence you only need two arguments and two rules describing the two possibilities. Either the number is zero, then its double is zero as well. Otherwise you have a difference of two s/2 for the double in every recursion but only one for the number:
nat_double(0,0).
nat_double(s(X),s(s(Y))) :-
nat_double(X,Y).
This yields the desired results:
?- nat_double(X,Y).
X = Y, Y = 0 ; % X=0, Y=0
X = s(0), % X=1
Y = s(s(0)) ; % Y=2
X = s(s(0)), % X=2
Y = s(s(s(s(0)))) ; % Y=4
X = s(s(s(0))), % X=3
Y = s(s(s(s(s(s(0)))))) ; % Y=6
X = s(s(s(s(0)))), % X=4
Y = s(s(s(s(s(s(s(s(0)))))))) ; % Y=8
.
.
.
Or if you want to test a pair as suggested in the comments:
?- nat_double(s(s(0)),s(s(s(s(0))))).
true.
EDIT:
If you insist on the interface isDouble/3, as your comment suggests, then you could define it as a calling predicate for nat_double/2 like so:
isDouble(X,X,Y) :-
nat_double(X,Y).
That yields the desired result for your example:
?- isDouble(s(s(0)),s(s(0)),s(s(s(s(0))))).
true.

Prolog - Allowing lists L iff #L > n in setof/3

likes(russel, wittgenstein).
likes(whitehead, wittgenstein).
likes(godel, wittgenstein).
likes(hardy, ramanujan).
likes(littlewood, ramanujan).
at_least_three_fans :-
setof(X, Z^(fact(X, idol), fact(Z, idol)), admirers).
I'd like to have print only Z : Z = wittgenstein, i.e., I'd like to have only the idols that have 3 or more admirers. The output to the predicate above:
?- at_least_three_fans.
idol = wittgenstein,
admirers = [godel, russell, whitehead].
idol = ramanujan,
admirers = [littlewood, hardy].
The following occurred to me:
at_least_three([_,_|_]).
But how am I to use it?
Thank you.

Prolog returning false for summation query

I'm new to Prolog as I'm just starting to learn and write up my own small set of database rules. Using my own .pl file of database rules, I'm having a small problem with a query that I enter in Prolog, using these rules. Below shows my small database of rules:
staff(andy,18235,3).
staff(beth,19874,4).
staff(andy,18235,5).
staff(carl,16789,2).
staff(earl,34567,9).
sum([], 0).
sum([H|T], X) :-
sum(T, X1),
X is X1 + H.
getincome(Name, Income) :-
findall(Income,staff(Name,_,Income),Member),
sum(Member, Income).
As you can see, I have written a rule that finds the total income for a particular member of staff. This works very fine, as when I input:
?- getincome(andy, X).
The program always returns:
X = 8
As it should do, however whenever I instead input:
?- getincome(andy, 8).
This always returns false, when it should be true.
However when I also input:
?- getincome(andy, 3).
This returns true, due to already being in the database.
I'm just wondering, how could I modify this rule so that this could output true for the correct summation value, entered for any given staff (most particularly Andy), as opposed to the value already in the given database?
Ignore my question above!
Thanks for the help 'false'. I'm also having another issue, this time to do with working out and displaying the sum of the income for each member. I have modified my rules, in order to display this, as follows:
getincome(Name, I) :- staff(Name, _, _ ), findall(Income,staff(Name,_,Income),Member), sum(Member, I).
Whenever I enter the query:
?- getincome(X, Y).
I keep getting duplicate results of staff (most notably Andy, of course), as show below:
X = andy,
Y = 8 ;
X = beth,
Y = 4 ;
X = andy,
Y = 8 ;
X = carl,
Y = 2 ;
X = earl,
Y = 9.
What changes can I make to avoid these duplicates?
library(aggregate) offers a clean interface to solve such kind of problems:
?- aggregate(sum(S), K^staff(E,K,S), I).
E = andy,
I = 8 ;
E = beth,
I = 4 ;
E = carl,
I = 2 ;
E = earl,
I = 9.
One way to do this is to use bagof to collect each set of incomes:
person_income(Name, Income) :-
bagof(I, X^staff(Name,X,I), Incomes), % Incomes for a given name
sumlist(Incomes, Income). % Sum the incomes
With results:
| ?- person_income(Name, Income).
Income = 8
Name = andy ? a
Income = 4
Name = beth
Income = 2
Name = carl
Income = 9
Name = earl
yes
| ?- person_income(andy, Income).
Income = 8
yes
| ?- person_income(Name, 8).
Name = andy ? a
no
| ?-
I named this person_income to emphasize that it's a relation between a person and their income, rather than getincome which is more of an imperative notion, and doesn't really reflect that you can do more with the relation than just "get the income". Also, I'm using SWI Prolog's sumlist/2 here. GNU Prolog has sum_list/2. And as #CappeliC indicates in his answer, SWI Prolog has a handy aggregate predicate for operations like this.

Prolog - get the factors for a given number doesn't stop?

I need to find the factors of a given number , e.g :
?- divisors2(40,R).
R = [40,20,10,8,5,4,2,1].
The code :
% get all the numbers between 1-X
range(I,I,[I]).
range(I,K,[I|L]) :- I < K, I1 is I + 1, range(I1,K,L).
% calc the modulo of each element with the given number :
% any x%y=0 would be considered as part of the answer
divisors1([],[],_).
divisors1([H|T],S,X):-divisors1(T,W,X),Z is X mod H,Z==0,S=[H|W].
divisors1([_|T],S,X):-divisors1(T,S,X).
divisors2(X,Result) :-range(1,X,Result1),divisors1(Result1,Result,X).
But when I run divisors2(40,RR). I get infinite loop and nothing is presented to the screen.
Why ?
Regards
You are asking why you get an infinite loop for the query divisors2(40,R). I almost wanted to explain this to you using a failure-slice. Alas ...
... the answer is: No, you don't get an infinite loop! And your program also finds an answer. It's
R = [1,2,4,5,8,10,20,40]
which looks reasonable to me. They are in ascending order, and you wanted a descending list, but apart from that, that is a perfect answer. No kidding. However, I suspect that you were not patient enough to get the answer. For 36 I needed:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1,2,3,4,6,9,12,18,36]
; ... .
Quite unusual ... for a list with at most 36 meager integers Prolog needed 10 744 901 605 inferences, that is less than 234. Does this ring a bell? In any case, there are problems with your program. In fact, there are two quite independent problems. How can we find them?
Maybe we are looking at the wrong side. Just go back to the query. Our first error was how we used Prolog's toplevel. We were very impressed to get an answer. But Prolog offered us further answers! In fact:
?- time(divisors2(36,R)).
% 10,744,901,605 inferences, 2248.800 CPU in 2252.918 seconds (100% CPU, 4778061 Lips)
R = [1,2,3,4,6,9,12,18,36]
; % 10 inferences, 0.000 CPU in 0.000 seconds (82% CPU, 455892 Lips) R = [1,2,3,4,6,9,12,18]
; % 917,508 inferences, 0.192 CPU in 0.192 seconds (100% CPU, 4789425 Lips)
R = [1,2,3,4,6,9,12,36]
; ... .
This gets too tedious. Maybe a tiny example suffices?
?- divisors2(6,R).
R = [1,2,3,6]
; R = [1,2,3]
; R = [1,2,6]
; R = [1,2]
; R = [1,3,6]
; R = [1,3]
; R = [1,6]
; R = [1]
; R = [2,3,6]
; R = [2,3]
; R = [2,6]
; R = [2]
; R = [3,6]
; R = [3]
; R = [6]
; R = []
; false.
More than enough! Maybe we stick to the minimal example [] and restate it:
?- divisors2(6,[]).
true
; false.
Clearly, that's not what we expected. We wanted this to fail. How to localize the problem? There is one general debugging strategy in Prolog:
If a goal is too general, specialize the program.
We can specialize the program by adding further goals such that above query still succeeds. I will add false and some (=)/2 goals. false is particularly interesting because it wipes out an entire clause:
?- divisors2(6,[]).
range(I,I,[I]) :- I = 6.
range(I,K,[I|L]) :- K = 6,
I < K,
I1 is I + 1,
range(I1,K,L).
divisors1([],[],X) :- K=6.
divisors1([H|T],S,X):- false,
divisors1(T,W,X),
Z is X mod H,
Z=0,
S=[H|W].
divisors1([_|T],S,X):- S = [], X = 6,
divisors1(T,S,X).
divisors2(X,Result) :- X = 6, Result = [].
range(1,X,Result1),
divisors1(Result1,Result,X).
Somewhere in the remaining part something is too general! In fact the recursive rule of divisors1/3 is too general. This new modified program of yours is called a slice that is a specialization of our original program.
Several ways to fix this, the most naive way is to add the corresponding condition like so:
divisors1([],[],_).
divisors1([H|T],S,X):-
divisors1(T,W,X),
0 =:= X mod H,
S=[H|W].
divisors1([H|T],S,X):-
divisors1(T,S,X),
0 =\= X mod H.
However, the performance of the program did not improve. To see this, I will again specialize this program:
divisors1([],[],_) :- false.
divisors1([H|T],S,X):-
divisors1(T,W,X), false,
0 =:= X mod H,
S=[H|W].
divisors1([H|T],S,X):-
divisors1(T,S,X), false,
0 =\= X mod H.
Thus: No matter what is there behind the false, this program will try at least 3 * 2^N inferences for a list of length N.
By putting the recursive goals last we can avoid this.
You have a bug here
divisors1([H|T],S,X):-
divisors1(T,W,X),
Z is X mod H,
Z==0,S=[H|W]. <=== here
If Z is Zero then S = [H|W] else S = W.
If you correct your range (use a cut for the end-of-recursion clause), you will get it sort of working. You do not immediately succeed upon finding all divisors though.
A solution using your general idea, but also built-ins between/3 and bagof/3 (to make typing a bit easier):
divisors(X, Divs) :- bagof(D, divs(X,D), Divs).
divs(X,D) :- between(1,X,D), 0 is X mod D.
Please note that this solution returns the divisors in increasing order.

Cryptogram solver in Prolog

I have to solve a cryptogram which looks like this:
ABC / DEF = 0.GHGHGH...
where length of variables may be different. Also repetition may be different (like 0.XYZXYZ...). I've written a piece of code which I thought will work but it doesn't:
cryp(A,B,C) :-
mn(A, X),
mn(B, Y),
mn(C, Z),
Z = X/Y.
mn([], 0).
mn([H|T], W) :- D is 10, mn(T, W1), length(T, D), P is 10^D, W is W1 + H*P.
I execute it as crypt([A,B,C], [D,E,F], [G,H]). I thought it will at least solve ABC / DEF = GH just to have any part of working solution but it doesn't work.
I don't have any clue how to do it even for one example input. I don't know how to represent 0.GHGHGH....
EDIT:
mn/2 is for converting list of digits to number ([1,2,3] -> 123).
Here is clp(fd)-way
?- use_module(library(clpfd)).
true.
?- Vars=[A,B,C,D,E,F,G,H], Vars ins 0..9, A#\=0, D#\=0, ((10*A + B)*10+C)*99 #= ((10*D+E)*10+F)*(10*G+H),all_distinct(Vars),forall(label(Vars),format('~w~n',[Vars])).
[1,0,8,2,9,7,3,6]
[1,0,8,3,9,6,2,7]
[1,3,0,2,8,6,4,5]
[1,3,0,4,9,5,2,6]
[1,3,8,2,9,7,4,6]
[1,3,8,5,0,6,2,7]
[1,6,0,4,9,5,3,2]
[1,8,0,3,9,6,4,5]
[1,8,0,4,9,5,3,6]
[2,0,4,3,9,6,5,1]
[2,5,9,4,0,7,6,3]
[2,8,4,3,9,6,7,1]
[2,8,7,4,5,1,6,3]
[2,8,7,6,9,3,4,1]
[2,9,0,6,3,8,4,5]
[3,1,0,4,9,5,6,2]
[3,1,0,6,8,2,4,5]
[3,6,0,4,9,5,7,2]
[3,6,0,7,9,2,4,5]
[3,8,0,4,9,5,7,6]
[4,0,8,5,6,1,7,2]
[4,0,8,7,9,2,5,1]
[4,9,3,5,6,1,8,7]
[5,0,4,6,9,3,7,2]
[5,0,4,7,9,2,6,3]
[5,1,8,6,9,3,7,4]
[5,7,4,6,9,3,8,2]
[5,7,4,9,0,2,6,3]
[5,9,4,7,2,6,8,1]
[6,8,0,9,3,5,7,2]
[7,5,6,9,2,4,8,1]
You might do the following identifications: let a=0.GHGHGHGH.... Then
a = 0.GHGH....
GH + a = GH.GHGHGHGH.... = 100 a
GH = 99 a
=> a = GH/99
So you can replace it by GH/99 in your formulas.
Similarly 0.XYZXYZXYZ = XYZ/999.

Resources