Prolog - bagof - case of no results - prolog

I have activity-users relations:
% Signature: activity(Name,Day)/2
% Purpose: describe an activity at the country club and the day it takes place
%
activity(swimming,sunday).
activity(ballet,monday).
activity(judu,tuesday).
activity(soccer,wednesday).
activity(art,sunday).
activity(yoga,tuesday).
% Signature: participate(Child_name,Activity)/2
% Purpose: registration details
%
participate(dany,swimming).
participate(dany,ballet).
participate(dana,soccer).
participate(dana,judu).
participate(guy,judu).
participate(shai,soccer).
I want to create query to get all the participants of activity.
So I use bagof:
activity_participants_list(Activity_Name,List) :- ( bagof(X, participate(X,Activity_Name), List)).
But I want to get List = [] when query: activity_participants_list(dancing,List)
So I added ; List = [] to the end of the function but then
when query
activity_participants_list(A,B) i also get empty list as an answer
And query activity_participants_list(A, []) return true.
I also would like to get yoga with empty list as an answer to activity_participants_list(A,B)
What is the right way to do it?

As you found, bagof fails when the goal has no solutions. This is dumb design, unfortunately. As you also found, ; is tricky to use. Try not to be tempted to use it.
findall works similarly to bagof, and it does give an empty list of results if the goal has no solutions:
activity_participants(Activity, Participants) :-
activity(Activity, _), % we are interested in a concrete activity
findall(Participant, participate(Participant, Activity), Participants).
?- activity_participants(Activity, Participants).
Activity = swimming,
Participants = [dany] ;
Activity = ballet,
Participants = [dany] ;
Activity = judu,
Participants = [dana, guy] ;
Activity = soccer,
Participants = [dana, shai] ;
Activity = art,
Participants = [] ;
Activity = yoga,
Participants = [].
The role of the activity(Activity, _) goal is to bind Activity, this basically forces findall to succeed several times, grouped by activity. If we removed this goal, we wouldn't group by activity, and we would just get a list of all the people participating in any activity:
?- activity_participants(Activity, Participants).
Participants = [dany, dany, dana, dana, guy, shai].

Related

PROLOG - conditional function

How can I list list the names of courses having both male and female students registered for them.
CODE:
/*student(name,studnumb,age,sex)*/
student(braum,1234,22,male).
student(lux,7839,26,female).
student(ekko,1726,29,male).
student(kayle,1114,25,female).
student(darius,6654,36,male).
student(morgana,4627,20,female).
student(ashe,2563,25,female).
student(brand,9258,30,male).
findboys(GENDER):-
student(_, IS, SY, GENDER),
( var(GENDER)->true
; SY = male
),
takes(IS, IT),
teaches(TN,IT),
writeln([TN,course(IS, _)]),
fail
; true.
/*takes(studnum,modnum)*/
takes(1234,1111).
takes(7839,1111).
takes(1726,1111).
takes(1114,2345).
takes(6654,1111).
takes(4627,4588).
takes(2563,2222).
takes(9258,6534).
/*course(modnum,modname)*/
course(2222,maths).
course(2345,english).
course(1111,computerscience).
course(6654,spanish).
course(6789,antrophormism).
course(4588,teology).
Unfortunately, I can not achieve the right query or conditional to print the list the names of courses having both male and female students registered for them
With Prolog, think of it as a logical statement:
C is a class with both male and female students if has_male_students(C) and has_female_students(C)
You can write that as follows, thinking of the :- operator as the if:
has_both_mf(C) :- has_gender(C, male), has_gender(C, female).
Now you just have to figure the logic for a class having a specific gender:
C has gender G students if C has id CourseId, student Id takes course CourseId and student Id is gender G.
You can write that as:
has_gender(C, G) :-
course(CourseId, C),
takes(StudentId, CourseId),
student(_, StudentId, _, G).
Now you're going to get repeats because a class may have multiple casts of male and female students, so a given class solves the same logic in more than one way. I'll leave that as an exercise to get unique solutions. An easy way is to use the once/1 predicate in SWI Prolog (you can find it in the documentation).

Differentiating two people prolog

I'm new to Prolog, and I'm having trouble understanding the OR operator ";"
so this is an example below:-
/*attributes(Person,Eats,Footwear).*/
attributes( personA,
eats(fried;baked),
footwear(shoes;slippers)
).
attributes( personB,
eats(roasted;baked),
slippers
).
person(Person, Eats, Footwear) :-
attributes(Person,
Eats,
Footwear
).
so I need to differentiate between personA and personB, eg if I put in the query
person(Person, roasted, _).
since only personB has the attribute roasted, it should return Person = personB
then, eg if I put in the query
person(Person, baked, _).
since both A and B has the attribute baked, it should return Person = personA Person = personB
Can anyone explain how to properly make the rules for this. Thanks.
To get the result you want, you have to rewrite the knowledge base in this way (you cannot use ; inside a term):
footwear(personA,slippers).
footwear(personA,shoes).
footwear(personB,slippers).
eats(personA,fried).
eats(personA,baked).
eats(personB,roasted).
eats(personB,baked).
Then you query:
?- eats(Person,roasted).
Person = personB.
?- eats(Person,baked).
Person = personA
Person = personB
If you are into probabilistic programming, you can translate this program into a probabilistic program to get maybe some interesting results.

Accessing a particular element in a list

I am new to Prolog and wanted to access specific elements of the list.For eg-
L=[name,age,height,weight,gender]
How can I access age and height from this list as I want to compare them with some given values???
A list is not intended to be used as a record. Anyway, just use unification
L = [Name,Age,Height,Weight,Gender],
( Gender == male -> ... ; ... )

Prolog beginner here

hasAccount(Person,Bank,Amount) – the Person has an account at the Bank with the balance Amount,
lives(Person,City) – the Person lives in the City,
created(Person,Bank,Month,Year) – the Person opened an account at the Bank in Month of the Year.
With the predicates above, I want to try few things, say printing a list of names with accounts they have, I tried the following query.
?- hasAccount(Someone, Bank1, Balance1), hasAccount(Someone, Bank2, Balance2), not Bank1 = Bank2.
Someone = ann
Bank1 = metro_credit_union
Balance1 = 1000
Bank2 = toronto_dominion
Balance2 = 12000
Yes (0.00s cpu, solution 1, maybe more)
Someone = ann
Bank1 = toronto_dominion
Balance1 = 12000
Bank2 = metro_credit_union
Balance2 = 1000
Can you please explain why do I have reversed list again and what I can do to prevent this happening? Thank you!
why I have reversed list again
it happens since you're not stating in your query enough info to break symmetry.
You can use instead
?- hasAccount(Someone, Bank1, Balance1), hasAccount(Someone, Bank2, Balance2), Bank1 #< Bank2.
or some builtin, like findall/3 (or bagof/3)

Prolog. Count members in database or List

I have a database i want to write a query to return the total members in the database
member(paul,100). member(john,101). member(ryan,102). member(jabouki,103).
Should Return 4
I also have a list and i want to return the number of persons in the list
memberlist([ant,cat,sat,bat]).
Should Return 4
Query I have thus far that only return the items in the database and list.
member(MemberName,_).
memberlist(Y)
findall(X, member(X,_), L), length(L,N).
Will give you the following results:
L = [paul, john, ryan, jabouki],
N = 4.
Greetings
Solick

Resources