Proper way to Declare and Search List in Prolog? - 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

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 predicate where two items are associated but not equivalent

I haven't programmed in Prolog for years and am struggling with a simple block of test code (I'm trying to solve a logic puzzle for fun...):
aboard(jack, blackbird).
aboard(jim, blackbird).
aboard(donna, blackbird).
aboard(david, north_star).
aboard(sandy, north_star).
shipmates(A, B) :- A \= B, aboard(A, X), aboard(B, X).
shipmates1(A, A) :- !, fail.
shipmates1(A, B) :- aboard(A, X), aboard(B, X).
The shipmates and shipmates1 rules are two different attempts to accomplish the following: I want to pair all passengers who are on the same ship but are not equivalent to each other.
For example, I want shipmates(jack, jack). to be false.
When I query this with fully-qualified arguments, I get the expected answers:
3 ?- shipmates(jack, david).
false.
4 ?- shipmates(jack, jack).
false.
5 ?- shipmates(jack, jim).
true.
However, when I want all of Donna's shipmates, it doesn't seem to work:
6 ?- shipmates(donna, X).
false.
I was expecting:
X = jack ;
X = jim ;
NOTE: I get the same wrong results with shipmates1.
So please take pity on a very amateur Prolog programmer (who is not doing homework for a class!) What very obvious thing am I doing wrong?
Version: SWI-Prolog (threaded, 64 bits, version 8.0.2)
Try:
shipmates(A, B) :-
aboard(A, X),
aboard(B, X),
A \= B.
By calling the aboard/2 predicate before the A \= B goal, you ensure that both A and B will be instantiated, thus making the comparison meaningful.

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).

How to define `not_a_parent()` predicate?

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,_) , []).

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