Search all fathers with conditions in Prolog - list

I have a family tree program in Prolog that contain next facts:
male(alex).
male(david).
male(peter).
etc
...
female(sofia).
etc
....
parent(alex, peter).
parent(sofia, peter).
etc
....
and some rules such as:
father(X, Y) :- parent(X, Y), male(X).
mother(X, Y) :- parent(X, Y), female(X).
etc
....
I need to find all fathers who has two or more children.
I think that I need to go through all fathers im my program, put their children to list and count it's length, then if >=2 I add this father to another list or just print him and go further.

Although in the general case when you want to find all fathers with at least N children your approach would be required, a much simpler approach is available when you are dealing with only two children: find a child twice, and see if the two children are different.
In Prolog this rule would look like this:
father_of_two(X) :-
father(X, A),
father(X, B),
A \= B.
All you need to do now is collecting all people who match father_of_two predicate into a list.

Related

Counting how many elements in a list of lists satisfy a predicate

Given a list of lists of integers, e.g. [[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]], I want to go over each sublist and count how many of them sum to 15. In this case that would be 1, for the sublist [3,10,2].
I am aware of the predicate aggregate_all/3, but I'm having trouble writing a predicate to check each element of the list, what I have now is something like
fifteens([X|Xs]) :-
sum_list(X, 15),
fifteens(Xs).
and within another predicate I have:
aggregate_all(count, fifteens(Combinations), Value).
where Combinations is the list of lists of integers in question.
I know my fifteens predicate is flawed since it's saying that all elements of the nested list must sum to 15, but to fix this how do I take out each element of Combinations and check those individually? Do I even need to? Thanks.
First of all your fifteens/2 predicate has no because for empty list and thus it will always fails because due to the recursion eventually fifteens([]) will be called and fail.
Also you need to change completely the definition of fifteens, currently even if you add base case, it says check ALL elements-sublists to see if they sum to 15. That's Ok but I don't see how you could use it with aggregate.
To use aggregate/3 you need to express with fifteens/2, something like: for every part of my combinations list check separately each sublist i.e each member:
ifteens(L) :-
member(X,L),
sum_list(X, 15).
Now trying:
?- aggregate_all(count, ifteens([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]]), Value).
Value = 1.
This is a job for ... foldl/4. Functional programming idioms in logic programming languages? Yes, we can!
First, summing the summable values of a list:
sum_them(List,Sum) :-
foldl(sum_goal,List,0,Sum).
sum_goal(Element,FromLeft,ToRight) :-
must_be(number,Element),
must_be(number,FromLeft),
ToRight is Element+FromLeft.
Then, counting the ones that sum to 15:
count_them(List,Count) :-
foldl(count_goal,List,0,Count).
count_goal(Element,FromLeft,ToRight) :-
must_be(list(number),Element),
must_be(number,FromLeft),
sum_them(Element,15) -> succ(FromLeft,ToRight) ; FromLeft = ToRight.
Does it work? Let's write some unit tests:
:- begin_tests(fifteen_with_foldl).
test("first test",true(R==1)) :-
count_them([[3,10],[3,10,2],[5],[5,2],[5,3],[5,3,2],[5,3,10]],R).
test("test on empty",true(R==0)) :-
count_them([],R).
test("test with 2 hist",true(R==2)) :-
count_them([[15],[],[1,1,1,1,1,10]],R).
:- end_tests(fifteen_with_foldl).
And so:
% PL-Unit: fifteen_with_foldl ... done
% All 3 tests passed
true.

Unifying list of lists prolog

I googled this but cant find the answer, so here you go:
I have this function in prolog:
ing(Lis) :- findall(I,( recipe2(_,ingredients(I,_)) ),Lis).
This function search and returns me a list of lists like this:
L = [['wheat flour', egg, salt], ['wheat flour', cheese, olives, tomato, salt, basil], ['wheat flour', potatoes, salt], [milk, egg, sugar]].
I want to unify that list of lists in only one list, so i can get out duplicates. I know i have to use recursion, but thats all i know.
Thanks in advance.
You may simply modify the predicate like such:
ing(Lis) :-
setof(E, X^Y^I^( recipe2(X, ingredients(I,Y)), member(E, I) ), Lis).
member/2 is a built-in predicate that unifies the first argument with an element of a list in the second argument. It is non-deterministic.
The use of X^Y^I^ are existential quantifiers to ensure that you only get your results in one solution. It essentially says,
There exists an X, Y, and I for any element E that is a part of an
ingredient list, (I).
Using setof/3 also ensures that any solution you get will be a collection of unique elements.
Documentation (SWI-Prolog) for member/2 and setof/3

Prolog Ancestor with List

I searched around and couldn't find the answer. I'm having trouble making a genealogy list.
So, I have some is_a relations, for example:
is_a(cow, animal).
is_a(calf, cow).
is_a(dog, animal).
.... etc.
I want to have a procedure that does the following:
toAnimal(cow, X).
that outputs
X= [calf, cow, animal].
Basically, if I give it an input(cow), then it will go from cow until animal and add every step to the list.
So far, I have this:
toAnimal(A, B) :- is_a(A,B).
toAnimal(A, B) :- is_a(A, X), toAnimal(X, B).
The output of this is would be
X= cow;
X = animal;
false
How would I get this to become a list?
EDIT:
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAnimal(X,Y):-
findall(X, descend('animal', X), Y).
I have updated it to this after looking at the suggestion. However, how do I get the list to print? I'm still new to prolog. The findall page said that it would return the list, but it is not doing so for me.
toAnimal(calf, Y)
outputs:
false.
EDIT:
It now returns an empty list. I'm not sure what the issue is here. I have not changed the code at all, so the output should not change but it has.
EDIT:
Thanks MrBratch for the response.
I made the suggested changes, but I now have another issue.
For example,
if I have the relations:
is_a(calf, cow).
is_a(calf, animal).
is_a(cow, cool).
is_a(cool, awesome).
But I ONLY want the path from calf to awesome.
The code will give me the ALL possible paths from calf,x.
For example,
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAwesome(A,Y) :-
findall(X, descend(calf, X), Y).
will give me a list Y that has
[cow,animal,cool,awesome].
but what I want is
[calf,cow,cool,awesome].
How do I filter the other paths?
and also add the starting point? I suppose I can append calf to the beginning as the head, but how do I ignore the other paths?
EDIT:
Thanks for the help
I figured it out, but I lose the end path and start path. For example,
L contains cow,cool.
But calf and awesome are not there. I tried appending but I don't really understand the syntax. I'm not allowed to do append(X,L,anewlist)?
descend(X,Y) :- is_a(X,Y).
descend(X,Y) :- is_a(X,Z), descend(Z,Y).
toAnimal(A,B) :-
setof(X, (descend(A,X), descend(X,'awesome')), B).
--> append(A, L,anewlist).
?? Is this line not allowed here? How else would I do it? or is there a simpler way to just add it from the beginning
Here it is. (NOTE: you don't need descend predicate to figure out the path of a particular branch of the tree)
is_a(calf, cow).
is_a(calf, animal).
is_a(cow, cool).
is_a(cool, awesome).
path(X,Y,[Z|T]) :- \+ is_a(X,Y), is_a(X,Z), path(Z,Y,T).
path(X,Y,[Y]) :- is_a(X,Y).
find_path(X,Y,[X|L]) :- path(X,Y,L).
Usage:
| ?- find_path(calf,awesome,L).
L = [calf,cow,cool,awesome] ? ;
This sample more or less does what you want:
is_a(cow, animal).
is_a(calf, cow).
is_a(dog, animal).
is_a(snoopy, dog).
is_a(lassie, collie).
is_a(collie, dog).
toAnimal3( X, [X,animal] , animal ):- is_a( X, animal).
toAnimal3( X, [X|R], R ):- is_a( X, Y), toAnimal3(Y, R, _).
:- initialization(main).
main :- toAnimal3( lassie, A, B), write(A), write(B).
When run, this is the output:
[lassie,collie,dog,animal][collie,dog,animal]
Tested it online using this Prolog online interpreter
POST EDIT: Ah, that was it! I should've written "[X,animal]" instead of "[X|animal]" for the first clause! Thanks galore to #mbratch , now the program does exactly what was intended.
toAnimal(X,Y) :- setof(X, descend('animal', X), Y). should do it. Or findall/3.
Info and some examples of bagof, setof, findall.
But remember that you are asking for descend(animal, X) so it won't match the fact is_a(dog, animal) for example, which descend(X, animal) will. You need to make descend to search both sides, or simply be sure that your is_a facts say animal just on left side.
If you want to filter you could do
toAnimal(X,Y) :- setof(X, (descend('animal', X), not(X = animal)), Y).
but you are getting animal as a result because what I mentioned before.

Add fact to list if it's not already in

I need to construct a list based on facts I have. For example I have a course list like this :
attend(student1,c1).
attend(student1,c2).
attend(student2,c1).
attend(student2,c3).
Now I want to have a predicate courselist/2 which returns a list of courses for a given student. Of course every course should be in this list only once. I can't use built-in predicates like findall but I can use member or append. So far I have something like this :
courselist(S,R) :- attend(S,C), member(C,R), courselist(S,R).
courselist(S,R) :- attend(S,C), append([C],L,R), courselist(S,R).
and I know that's wrong but I don't know how to find all facts without getting into an endless loop.
It's a silly restriction that you cannot use findall/3, because it is a natural solution for such a problem. You can do it manually like this:
student_courses(Student, Courses) :-
student_courses(Student, [], Courses).
student_courses(S, Cs0, Cs) :-
( attend(S, C), \+ member(C, Cs0) ->
student_courses(S, [C|Cs0], Cs)
; Cs = Cs0
).
Example query:
?- student_courses(student2, Cs).
Cs = [c3, c1].
Note that this is not a true relation, since this particular solution does not appear in the following more general query:
?- student_courses(Student, Cs).
Student = student1,
Cs = [c2, c1].
I leave it as an exercise for you to implement it in such a way that the most general query yields all correct results. Also notice the more readable and relational predicate names.

Prolog list adding

--the question has been edited--
Using this data, I need to create a list:
team(milan,1).
team(napoli,2).
team(lazio,3).
team(roma,4).
team(inter,4).
team(juventus,5).
So, given a query like:
check([milan,lazio,roma,inter]).
make a new list with their respective team number.
X=[1,3,4,4]
What I'm trying to do is creating a list, adding elements one at a time.
check([H|T]) :-
team(H,R),
append([R],_, X),
check(T).
Could someone help me complete this?
You need to find all the team numbers for which the name of the team is a member of the list of team names that you are interested in:
?- findall(Number, (
team(Name, Number),
member(Name, [milan, lazio, roma, inter])), Numbers).
Numbers = [1, 3, 4, 4].
To return the numbers in a given order, just apply member/2 before team/2, in this case member/2 generates names (in the given order), and team/2 maps them to numbers:
?- findall(Number, (
member(Name, [lazio, milan, inter]),
team(Name, Number)), Numbers).
Numbers = [3, 1, 4].
A lot of time since I used Prolog but an answer -more or less- would look like:
check([]) :- true.
check([X]) :- team(X,_).
check([X,Y]) :- team(X,N), team(Y,M), N < M.
check([X,Y|T]) :- check(X,Y), check([Y|T]).
See this question for a very similar problem.
From what you say you might be better off making a list and then sorting it. That way you'd know the list is in order. Of course it's tricky in that you are sorting on the team ranks, not the alphabetic order of their names.
But the question you asked is how to check the list is in sorted order, so let's do it.
check([ ]). % just in case an empty list is supplied
check([_]). % singleton lists are also in sort order
check([H1,H2|T]) :-
team(H1,R1),
team(H2,R2),
R1 <= R2,
check([H2|T]).
Note that the recursion reduces lists with at least two items by one, so the usual termination case will be getting down to a list of length one. That's the only tricky part of this check.
Added in response to comment/question edit:
Sure, it's good to learn a variety of simple "design patterns" when you are getting going with Prolog. In this case we want to "apply" a function to each item of a list and build a new list that contains the images.
mapTeamRank([ ],[ ]). % image of empty list is empty
mapTeamRank([H|T],[R|S]) :-
team(H,R),
mapTeamRank(T,S).
So now you have a predicate that will turn a list of teams LT into the corresponding list of ranks LR, and you can "check" this for sorted order by calling msort(LR,LR):
check(LT) :-
mapTeamRank(LT,LR),
msort(LR,LR).