How do I go through a database in prolog - prolog

I have a database composed of symptoms of a disease and the disease to which these symptoms belongs as follows.
disease([dordecabeca,febre,dormuscular,dorgarganta,cansaco],gripe).
disease([febre,dordecabeca,dorolhos,manchas,nauseas],dengue).
disease([febre,coceira,dordecabeca,dordebarriga,perdadeapetite],catapora).
disease([febre,dordecabeca,fadiga,perdadeapetite,inchacorosto],caxumba).
disease([congestaonasal,gargantairritada,espirros,febre,tosse],resfriado).
treatment([evitarmedicamentosdeaspirina,respouso],dengue).
treatment([evitarmedicamentosdeaspirina,repouso,paracetamol],dengue).
treatment([repouso,evitarpessoas,semmedicamento],catapora).
treatment([repouso,evitarpessoas,medicamento],catapora).
treatment([repouso,evitarpessoas],caxumba).
treatment([repouso,boaalimentacao],gripe).
treatment([repouso,boalimentacao,medicamentos],gripe).
treatment([repouso,boaalimentacao,semmedicamentos],refriado).
treatment([repouso,boaalimentacao,medicamento],resfriado).
symptoms(L1,X):-disease(L1,X).
treatdisease(L1,L2,Y):-symptoms(L1,Y),treatment(L2,Y).
With the symptons predicate, I can visualize all the symptoms and the corresponding disease. And with the predicate treatdisease, I can see the treatment based on the disease common to the two bases.
symptoms(L1,X):-disease(L1,X).
treatdisease(L1,L2,Y):-symptoms(L1,Y),treatment(L2,Y).
But what if I were to compare an entry list with the underlying disease like I would?
If it was just a list I already have the predicate, but on a multidimensional basis I have no idea how to go.
For example if I came in with:
?searchdisease([dordecabeca,febre,dormuscular,dorgarganta],Disease).
How do I go through the bases using this list with the database?
So I have predicates to pick up the different elements between two lists and a predicate to get the equal elements between two lists, but I do not know how to use them when the list is in a subset. Follow the predicates.
%---------------------------------------------------------
%Predicate to pick up equal elements between two lists.
equalelements([],[]).
equalelements([X|Xs0],Ys0) :-
tpartition(=(X),Xs0,Es,Xs),
if_(Es=[], Ys0=Ys, Ys0=[X|Ys]),
equalelements(Xs,Ys).
tpartition(P_2,List,Ts,Fs) :-
tpartition_ts_fs_(List,Ts,Fs,P_2).
tpartition_ts_fs_([],[],[],_).
tpartition_ts_fs_([X|Xs0],Ts,Fs,P_2) :-
if_(call(P_2,X), (Ts = [X|Ts0], Fs = Fs0),
(Ts = Ts0, Fs = [X|Fs0])),
tpartition_ts_fs_(Xs0,Ts0,Fs0,P_2).
if_(If_1, Then_0, Else_0) :-
call(If_1, T),
( T == true -> call(Then_0)
; T == false -> call(Else_0)
; nonvar(T) -> throw(error(type_error(boolean,T),_))
; /* var(T) */ throw(error(instantiation_error,_))
).
=(X, Y, T) :-
( X == Y -> T = true
; X \= Y -> T = false
; T = true, X = Y
; T = false,
dif(X, Y) % ISO extension
% throw(error(instantiation_error,_)) % ISO strict
).
equal_t(X, Y, T):-
=(X, Y, T).
%------------------------------------------------------------
%Predicate to pick up different elements between two lists.
displaydifference([],[],[]).
displaydifference(L1,L2,L4):-concatenate(L1,L2,L3), remove_dups(L3,L4).
concatenate(L1, L2, NL) :-
append(L1, L2, L12),
msort(L12, NL).
remove_dups([], []).
remove_dups([X], [X]).
remove_dups([X,Y|T], [X|R]) :-
X \= Y,
remove_dups([Y|T], R).
remove_dups([X,X|T], R) :-
skip(X, T, WithoutX),
remove_dups(WithoutX, R).
skip(_,[],[]).
skip(X, [X|T], T).
skip(X, [Y|T], [Y|T]) :- X \= Y.

Not sure to understand what do you exactly want obtain with searchdisease/2.
I suppose that you want a predicate that, given a list of symptoms, unifies the second parameter with one or more diseases with symptoms that are a superset of the first parameter.
In that case, I propose
subList([], _).
subList([H | T], S) :-
member(H, S),
subList(T, S).
searchdisease(Symptoms, Disease) :-
disease(Ls, Disease),
subList(Symptoms, Ls).
If you call searchdisease([dordecabeca,febre,dormuscular,dorgarganta],Disease), you unify Disease with gripe because [dordecabeca,febre,dormuscular,dorgarganta] is a subset of the symptoms of gripe.
If you call searchdisease([febre],D), you unify D with gripe and, trying again with backtracking, dengue, catapora, caxumba and resfriado, because febre is a symptom of all of this five diseases.
En passant: I don't understand the usefulness of symptoms/2; why don't you simply use disease/2?

Related

PROLOG. Get all objects from list matching a pattern

I am a beginner in prolog and i have a problem with getting objects from list matching a pattern.
If i have a list [1,2,3,4,5,1,1] . I want to use a predicate selectAll(Elem,List,X).
Where i use ?- selectAll(1,[1,2,3,4,5,1,1],X), I get X =[1,1,1], but i also want to use data structures inside the predicate, not only atoms.
I originally wrote this predicate for getting all matching elements, but it works only for simple cases, where only atoms are used:
selectAll(_, [], []).
selectAll(X, [X | LIST], [X | RES]):-
selectAll(X, LIST, RES),!.
selectAll(X, [H | LIST], RES):-
selectAll(X, LIST, RES).
When i use this test predicate, everything works fine. I get X=[1,1,1], the result i want.
test_select_all:-
selectAll(1, [1,2,3,4,5,1,1], X),
write(X),nl,
fail.
I have a data structure called kv_pairs(A,B) where A and B contain atoms of any type.
So when i use the selectAll predicate for this datatype, i get unwanted results. X = [kv_pair(1,a)]. It selects only 1 element at most.
test_select_all_dict:-
selectAll(kv_pair(1,_), [kv_pair(1, a), kv_pair(1, b),kv_pair(3, jkak), kv_pair(15, asdjk), kv_pair(1, c)], X),
write(X),nl,
fail.
I then created this predicate, specifically for finding list elements, where all types are kv_pairs
selectAll(_, [], []).
selectAll(kv_pair(Arg, _), [kv_pair(Arg,_) | LIST], [kv_pair(Arg,_) | RES]):-
selectAll(kv_pair(Arg, _), LIST, RES),!.
selectAll(kv_pair(Arg, X), [kv_pair(A, B) | LIST], RES):-
selectAll(kv_pair(Arg, X), LIST, RES).
But then i get also unwanted results.
X = [kv_pair(1,_8378),kv_pair(1,_8396),kv_pair(1,_8426)]
How can i get
X = [kv_pair(1,a),kv_pair(1,b),kv_pair(1,c)]?
Any help would be appreciated.
You can use the ISO predicate subsumes_term/2 to undo bindings after unification:
select_all(Pattern, List, Result) :-
select_all_loop(List, Pattern, Result).
select_all_loop([], _, []).
select_all_loop([X|Xs], P, R) :-
( subsumes_term(P, X)
-> R = [X|Ys]
; R = Ys ),
select_all_loop(Xs, P, Ys).
Examples:
?- select_all(kv_pair(1,_), [kv_pair(1,a), kv_pair(1,b), kv_pair(3,c), kv_pair(4,d), kv_pair(1,c)], R).
R = [kv_pair(1, a), kv_pair(1, b), kv_pair(1, c)].
?- select_all(p(1,Y), [p(1,a), p(1,b), p(2,b), p(1,c)], L).
L = [p(1, a), p(1, b), p(1, c)].
?- select_all(p(X,b), [p(1,a), p(1,b), p(2,b), p(1,c)], L).
L = [p(1, b), p(2, b)].

Write a Prolog predicate next(X,List,List1)

Prolog predicate next(X, List,List1), that returns in List1 the next element(s) from List that follows X, e.g., next(a,[a,b,c,a,d],List1), will return List1=[b,d].
I have tried following:
next(X, [X,Y|List], [Y|List1]) :- % X is the head of the list
next(X, [Y|List], List1).
next(X, [Y|List], List1) :- % X is not the head of the list
X \== Y,
next(X, List, List1).
next(_,[], []).
First, whenever possible, use prolog-dif for expressing term inequality!
Second, the question you asked is vague about corner cases: In particular, it is not clear how next(E,Xs,Ys) should behave if there are multiple neighboring Es in Xs or if Xs ends with E.
That being said, here's my shot at your problem:
next(E,Xs,Ys) :-
list_item_nexts(Xs,E,Ys).
list_item_nexts([],_,[]).
list_item_nexts([E],E,[]).
list_item_nexts([I|Xs],E,Ys) :-
dif(E,I),
list_item_nexts(Xs,E,Ys).
list_item_nexts([E,X|Xs],E,[X|Ys]) :-
list_item_nexts(Xs,E,Ys).
Let's see some queries!
?- next(a,[a,b,c,a,d],List1).
List1 = [b,d] ;
false.
?- next(a,[a,a,b,c,a,d],List1).
List1 = [a,d] ;
false.
?- next(a,[a,a,b,c,a,d,a],List1).
List1 = [a,d] ;
false.
Note that above queries succeed, but leave behind useless choicepoints.
This inefficiency can be dealt with, but I suggest figuring out more complete specs first:)
This version is deterministic for the cases given by #repeat using if_/3 and (=)/3. It shows how purity and efficiency can coexist in one and the same Prolog program.
next(E, Xs, Ys) :-
xs_e_(Xs, E, Ys).
xs_e_([], _E, []).
xs_e_([X|Xs], E, Ys) :-
if_(X = E, xs_e_xys(Xs, E, Ys), xs_e_(Xs, E, Ys)).
xs_e_xys([], _E, []).
xs_e_xys([X|Xs], E, [X|Ys]) :-
xs_e_(Xs, E, Ys).
%xs_e_xys([X|Xs], E, [X|Ys]) :- % alternate interpretation
% xs_e_([X|Xs], E, Ys).

How to check if a list is a non-empty sublist of another list in Prolog

I am trying to create an included_list(X,Y) term that checks if X is a non-empty sublist of Y.
I already use this for checking if the elements exist on the Y list
check_x(X,[X|Tail]).
check_x(X,[Head|Tail]):- check_x(X,Tail).
And the append term
append([], L, L).
append([X | L1], L2, [X | L3]) :- append(L1, L2, L3).
to create a list, in order for the program to finish on
included_list([HeadX|TailX],[HeadX|TailX]).
but I am having problems handling the new empty list that I am trying to create through "append" (I want to create an empty list to add elements that are confirmed to exist on both lists.)
I have found this
sublist1( [], _ ).
sublist1( [X|XS], [X|XSS] ) :- sublist1( XS, XSS ).
sublist1( [X|XS], [_|XSS] ) :- sublist1( [X|XS], XSS ).
but it turns true on sublist([],[1,2,3,4)
Since you're looking for a non-contiguous sublist or ordered subset, and not wanting to include the empty list, then:
sub_list([X], [X|_]).
sub_list([X], [Y|T]) :-
X \== Y,
sub_list([X], T).
sub_list([X,Y|T1], [X|T2]) :-
sub_list([Y|T1], T2).
sub_list([X,Y|T1], [Z|T2]) :-
X \== Z,
sub_list([X,Y|T1], T2).
Some results:
| ?- sub_list([1,4], [1,2,3,4]).
true ? a
no
| ?- sub_list(X, [1,2,3]).
X = [1] ? a
X = [2]
X = [3]
X = [1,2]
X = [1,3]
X = [1,2,3]
X = [2,3]
(2 ms) no
| ?- sub_list([1,X], [1,2,3,4]).
X = 2 ? a
X = 3
X = 4
(2 ms) no
Note that it doesn't just tell you if one list is a sublist of another, but it answers more general questions of, for example, What are the sublists of L? When cuts are used in predicates, it can remove possible valid solutions in that case. So this solution avoids the use of cut for this reason.
Explanation:
The idea is to generate a set of rules which define what a sublist is and try to do so without being procedural or imperative. The above clauses can be interpreted as:
[X] is a sublist of the list [X|_]
[X] is a sublist of the list [Y|T] if X and Y are different and [X] is a sublist of the list T. The condition of X and Y different prevents this rule from overlapping with rule #1 and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
[X,Y|T1] is a sublist of [X|T2] if [Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #1 (which can result in any single solution being repeated more than once).
[X,Y|T1] is a sublist of [Z|T2] if X and Z are different and [X,Y|T1] is a sublist of T2. The form [X,Y|T1] ensures that the list has at least two elements so as not to overlap with rule #2, and the condition of X and Z different prevents this rule from overlapping with rule #3 (which can result in any single solution being repeated more than once) and greatly reduces the number of inferences required to execute the query by avoiding unnecessary recursions.
Here is what you an do:
mysublist(L,L1):- sublist(L,L1), notnull(L).
notnull(X):-X\=[].
sublist( [], _ ).
sublist( [X|XS], [X|XSS] ) :- sublist( XS, XSS ).
sublist( [X|XS], [_|XSS] ) :- sublist( [X|XS], XSS ).
Taking a reference from this:
Prolog - first list is sublist of second list?
I just added the condition to check if it was empty beforehand.
Hope this helps.
If order matters. Example [1,2,3] is sublist of [1,2,3,4] but [1,3,2] not.
You can do something like this.
sublist([],L).
sublist([X|L1],[X|L2]):- sublist(L1,L2)
I would use append :
sublist(X, []) :-
is_list(X).
sublist(L, [X | Rest]) :-
append(_, [X|T], L),
sublist(T, Rest).
Basically we can check if M is a sublist of L if M exists in L by appending something on its back and/or its front.
append([], Y, Y).
append([X|XS],YS,[X|Res]) :- append(XS, YS, Res).
sublist(_, []).
sublist(L, M) :- append(R, _, L), append(_, M, R).

Sort a sublist prolog

I have this list in Prolog:
[[13,Audi A3,11.11.2011,75000,berlina,audi,12100,verde pisello,[4wd],3.0000133333333334],[11,santafe,11.11.2011,80000,fuoristrada,audi,2232232,verde pisello,[Metalizzata,Sedile in Pelle,4wd],7.0000125]]|
I want to sort this list for last value of sublist, as example I want to have this result:
[[11,santafe,11.11.2011,80000,fuoristrada,audi,2232232,verde pisello,[Metalizzata,Sedile in Pelle,4wd],7.0000125],[13,Audi A3,11.11.2011,75000,berlina,audi,12100,verde pisello,[4wd],3.0000133333333334]]
predsort it's your friend. Then sort is easy, but to sell an Audi verde pisello will remain very, very hard...
sort_on_last(List, Sorted) :-
predsort(compare_last, List, Sorted).
compare_last(R, X, Y) :-
last(X, Xl),
last(Y, Yl),
compare(R, Xl, Yl).
To try it:
test :- sort_on_last(
[[11,santafe,'11.11.2011',80000,fuoristrada,audi,2232232,'verde pisello',['Metalizzata','Sedile in Pelle','4wd'],7.0000125],
[13,'Audi A3','11.11.2011',75000,berlina,audi,12100,'verde pisello',['4wd'],3.0000133333333334]
], S),
maplist(writeln, S).
?- test.
[13,Audi A3,11.11.2011,75000,berlina,audi,12100,verde pisello,[4wd],3.0000133333333334]
[11,santafe,11.11.2011,80000,fuoristrada,audi,2232232,verde pisello,[Metalizzata,Sedile in Pelle,4wd],7.0000125]
true.
A particularity of predsort/3: it acts as sort/2, thus remove duplicates.
To avoid this problem, compare_last/3 can be changed, avoiding return =, in this way:
compare_last(R, X, Y) :-
last(X, Xl),
last(Y, Yl),
( Xl < Yl -> R = (<) ; R = (>) ).

Prolog: Getting unique atoms from propositional formulas

I can easily write a predicate to get unique elements from a given list in Prolog e.g.
no_doubles( [], [] ).
no_doubles( [H|T], F ) :-
member( H, T ),
no_doubles( T, F ).
no_doubles( [H|T], [H|F] ) :-
\+ member( H, T ),
no_doubles( T, F ).
However, how can you do the same thing but for something other than a normal list i.e. not something like [a,b,c...]? So in my case, I want to extract unique atoms for a propositional formula e.g. unique_atoms(and(x,and(x,y),z),[x,y,z]). is satisfied. Do you use recursion just like in my no_doubles example but for a formula like this?
Any ideas are welcomed :). Thanks.
So you need to process a general term (i.e. a tree structure) and get a list of its atomic leaf nodes, without duplicates. Does the result list have to have a specific order (e.g. depth-first left-to-right), or is this not important?
If you have an option to use variables instead of atoms in your formulas then you can use the (SWI-Prolog) builtin term_variables/2, e.g.
?- term_variables(and(X, and(X, Y), Z), Vars).
Vars = [X, Y, Z].
Otherwise you have to go with a solution similar to:
term_atoms(Term, AtomSet) :-
term_to_atomlist(Term, AtomList),
list_to_set(AtomList, AtomSet).
term_to_atomlist(Atom, [Atom]) :-
atom(Atom),
!.
term_to_atomlist(Term, AtomList) :-
compound(Term),
Term =.. [_ | SubTerms],
terms_to_atomlist(SubTerms, AtomList).
terms_to_atomlist([], []).
terms_to_atomlist([Term | Terms], AtomList) :-
term_to_atomlist(Term, AtomList1),
terms_to_atomlist(Terms, AtomList2),
append(AtomList1, AtomList2, AtomList).
Usage example:
?- term_atoms(f(x^a1+a3*a3/a4)='P'-l, Atoms).
Atoms = [x, a1, a3, a4, 'P', l].
You might want to extend it to deal with numbers and variables in the leaf nodes.
?- setof(X, member(X,[a,b,c,a,b,c]), L).
L = [a, b, c].
?- sort([a,b,c,a,b,c], L).
L = [a, b, c].
Propositional formulas:
get_atoms(X,[X]) :-
atom(X).
get_atoms(and(P,Q),Atoms) :-
get_atoms(P,Left),
get_atoms(Q,Right),
append(Left,Right,Atoms).
etc. Optimize using difference lists if necessary.
unique_atoms(P,UniqueAtoms) :- get_atoms(P,Atoms), sort(Atoms,UniqueAtoms).
A more direct way is to use sets.

Resources