How to define `not_a_parent()` predicate? - prolog

How to create such logic in GNU Prolog? How to define not_a_parent() predicate?
parent(john,chris).
parent(mary,chris).
not_a_parent(X) :- \+ parent(X,Y).
The interesting answer to the similar question is What is the logical 'not' in Prolog?. But I do not see how to implement it here.

This worked for me:
parent(john,chris).
parent(mary,chris).
parent(mary,suzanne).
parent(suzanne,jane).
parent(suzanne,peter).
parent(peter,rose).
parent(jerry,rose).
parent(jane,carl).
not_a_parent(NonParents) :- setof(Z,Y^parent(Y,Z),SetOfChildren),
findNonParents(SetOfChildren, NonParents, []),!.
findNonParents([],A,A).
findNonParents([H|SetOfChildren], NonParents, A):-
not(call(parent(H,_))),
findNonParents(SetOfChildren,NonParents,[H|A]).
findNonParents([_|SetOfChildren], NonParents, A):-
findNonParents(SetOfChildren,NonParents,A).
Result for querying not_a_parent(NonParents) is:
?- not_a_parent(NonParents).
NonParents = [rose, chris, carl].

You need to enumerate all the persons somehow. For example
not_a_parent(X) :- ( X = john ; X = mary ; X = chris ), \+ parent(X,_).
In any real program you would probably have some simple way to get all persons, and then you can do
not_a_parent(X) :- person(X), \+ parent(X,_).

You need to get all the instances of parent/2 such that X never unifies parent(X,_). findall(Template, Goal, Instances) executes the Goal until it fails and populate the list Instances with the terms that unify the Template. That way, if the list Instances is empty then parent(X,_) does not exist.
So your predicate would be like:
not_a_parent(X):- findall(_, parent(X,_) , []).

Related

PROLOG family tree: how can I output all the siblings and cousins?

Hi I am working on a PROLOG family tree question, and this is what I have so far:
/*1. Write Prolog clauses to express the following three relationships,
* given the parent/2 relationship: grandparent/2, sibling/2, cousin/2.*/
% clauses
parent(jill, amy).
parent(jill, tim).
parent(jill, john).
parent(amy, grace).
parent(amy, anna).
parent(tim, sam).
parent(tim, joel).
parent(tim, ben).
% rules
grandparent(X,Y) :-
parent(Z,Y),
parent(X,Z).
sibling(X, Y) :-
parent(Z, X),
parent(Z, Y).
cousin(X,Y) :-
parent(P, X),
parent(S, Y),
sibling(P, S).
When I put:
?- sibling(X, tim).
the output gives:
X = amy
but both john and amy are tim's sibling. The same problem happens with:
?- cousin(ben, X).
which gives:
X = grace
when both grace and anna are ben's cousins.
What changes do I need to make in order for the code to output all of tim's siblings and ben's cousins?
Thanks. :)
First of all, you've got a little bug over there.
You should correct the sibling rule - just a small hint here, try to use the rule as so
sibling(grace,grace)
and back to your issue, after you're getting first response click the ; or any of these ; n r space TAB keys, as the result you see is the first correct response. If you want to see the next correct result you need to use one of the keys above.
You can also try to use findall predicate to see all the results in the list
?- findall(X, cousin(grace, X),Z).
Z = [sam, joel, ben].

Prolog bidirectional predicate

I have to create family relations in Prolog for an assignment and I stumbled upon this problem.
man(john).
woman(lisa).
married(john,lisa).
?- married(john,X).
X = lisa.
?- married(X,john).
false.
How to make this predicate work in two ways?
If john is married to lisa, then lisa is married to john.
For facts I can only use gender, parent, and married:
man(john).
woman(lisa).
parent(john,steve).
parent(lisa,steve).
married(john,lisa).
That requirement makes this solution unusable for me. I can't just add relation wife(lisa,john). because I have to define wife, husband etc. by myself, like.
wife(X,Y) :- woman(X),married(X,Y).
Instead of trying to make married/2 bidirectional, you could define your predicate wife/2 accordingly. If the first argument of married/2 is always the husband and the second always the wife, just flip the arguments of the second goal:
wife(X,Y) :-
woman(X),
married(Y,X). % <- changed to Y,X instead of X,Y
With your given facts this yields the desired result:
?- wife(X,Y).
X = lisa,
Y = john.
If your facts also include pairs the other way around, e.g.:
woman(ann).
man(anton).
married(ann, anton).
you can include a second rule (namely your original rule) for wife/2 to deal with such pairs as well:
wife(X,Y) :-
woman(X),
married(Y,X).
wife(X,Y) :- % <- new rule
woman(X),
married(X,Y).
With the additional rule and facts the above query now yields:
?- wife(X,Y).
X = lisa,
Y = john ;
X = ann,
Y = anton.
If you can't add helper relation (it sounds like homework exercise), try to use predicate #< which operates on the "Standard Order of Terms":
married(A,B) :- A #< B, married(B,A).
Note, however, that this solution brings also some negative aspects (please read this answer).

Mutually referential Prolog rules in family tree

I've seen various implementations of family trees in Prolog, but I haven't found one that does what I would like to do, which is to define child and parent by referring to each other.
I want to do this because sometimes I have a fact that someone is a child of someone, at other times I have the fact that someone is the parent of someone. From either of these kinds of facts, I'd like to be able to ask who are parents and who are children.
My attempt at coding this is :-
parent(mary, fred).
child(john, peter).
child(paul, peter).
parent(P, C).
parent(P, C) :- child(C, P).
child (C, P).
child(C, P) :- parent(P, C).
This seems to work ok except that it will continue to give me the same results duplicated over and over. Eg :-
[3] ?- parent(peter, X).
true ;
X = john ;
X = paul ;
true ;
X = john ;
X = paul ;
true
Is there a way I can get it to stop after it has given me the full set of results once ?
More generally, is this kind of definition a weird thing to want to do (because of the mutual recursion) ? I want to be able to have facts of either parent or child, as reported, but also to infer the "opposite" relationship from these.
Thanks !
The issue with your program is that you are merging predicates.
parent/2 and child/2 are facts, you should not name a rule like a fact that is already defined in your program.
Rename the rules and all will work. Also, the base clause in your rules should add a condition, that match the fact, something like this:
parent(mary, fred).
child(john, peter).
child(paul, peter).
isparent(P, C):- parent(P, C).
isparent(P, C):- child(C, P).
ischild(C, P):- child(C, P).
ischild(C, P) :- parent(P, C).
Now the query:
?- isparent(peter, X).
X = john
X = paul
Also, don't use the complement rule in your condition, that's not necessary and will avoid you the recursion

Proper way to Declare and Search List in Prolog?

I'm new to Prolog. Is it possible to "declare" a list as a fact in Prolog, and then access it easily? I'm working on a family-tree type problem. To start, I would like to declare a bunch of males as belonging to a list of males. I then would like to be able to query if a particular person is a male. Here is the code I've written so far:
% ------------------------------------------------------------------------- %
% Facts:
males([john, joseph, aaron, peter, paul, mark, ben, adam, daniel]).
% ------------------------------------------------------------------------- %
% Rules:
% Member of a List
member(X, [X|_]).
member(X, [_|T]) :- member(X, T).
male(X) :- member(X, males).
However, my output is not as expected:
21 ?- male(john).
false.
I know my member() rules are working, since I can produce the following result:
23 ?- member(john, [peter, paul, john]).
true
Can anyone point me in the right direction? Thank you in advance.
The usual proper way to do this in Prolog would be to declare your list as a table of facts:
male(john).
male(joseph).
% etc
If you do it like this, you have already defined your male/1 and can query it either like this:
?- male(joseph).
true.
Or like this:
?- male(M).
M = john ;
M = joseph ;
% etc
You can start with Prolog by reading through the first chapter of "Learn Prolog Now!". Its first example is exactly the same as your problem.
I'm not an expert in prolog but I know a solution to this (could be not the only one)
male(X) :- males(Y), member(X, Y)
The way I understand it is that predicates doesn't return variables, they assign values to the parameters given, so Y now have been assigned the array of males and now you can look for X in Y

Match database items exactly once in Prolog?

Let's say there is a simple database of people in Prolog
person(john).
person(mary).
person(john).
person(susan).
I need to match the entires exactly once:
john-mary, john-john, john-susan, mary-john, mary-susan, john-susan
I tried coming up with something like this:
match:- person(X),!,person(Y), write(X),write(-), write(Y),nl.
run:- person(X), match(X), fail.
But it's matching many times, and matches a person to him/herself, which shouldn't be.
Basically, what I need is to iterate over all Xs and make Prolog to look strictly "below" for Ys.
A quick solution would be to number your people:
person(1, john).
person(2, mary).
person(3, john).
person(4, susan).
Then you could match people like this:
match(X-Y) :-
person(I, X), person(J, Y), I < J.
Since you have two john entries, I'm not sure any other solution is going to work. Normally you could fake an ordering using #>/2 but that would require your atoms to be unique, and since they aren't, it would prevent the john-john solution.
Edit: Since we're willing to use findall/3 to materialize the database of people, we can treat this as a list problem and find a functional solution. Let's get all the combinations in a list:
combinations([X|Rest], X, Y) :- member(Y, Rest).
combinations([_|Rest], X, Y) :- combinations(Rest, X, Y).
With this predicate in hand, we can find the solution:
combined_folks(People) :-
findall(P, person(P), Persons),
findall(X-Y, combinations(Persons, X, Y), People).
?- combined_folks(X).
X = [john-mary, john-john, john-susan, mary-john, mary-susan, john-susan].
That actually turned out to be pretty clean!
person(john).
person(mary).
person(john).
person(susan).
match :- findall(P,person(P),People), match_all(People).
match_all([_]) :- !.
match_all([P|People]) :- match_2(P,People), match_all(People).
match_2(_,[]) :- !.
match_2(P1,[P2|People]) :- format('~a-~a~n',[P1,P2]), match_2(P1,People).
?- match.

Resources