Prolog knowledge query - prolog

% A quiz team structure takes the form:
% team(Captain, Vice_captain, Regular_team_members).
% Captain and Vice_captain are player structures;
% Regular_team_members is a list of player structures.
% player structures take the form:
% player(First_name, Surname, details(Speciality,Recent_score)).
team(player(niall,elliott,details(history,11)),
player(michelle,cartwright,details(fashion,19)),
[player(peter,lawlor,details(science,12)),
player(louise,boyle,details(current_affairs,17))]).
I've been given the database above (I haven't copied over all the entries of people as it would be too long).
I've been asked to get the surname of any vice-captain whose team includes a captain or a regular team member whose speciality is science.
I can get the surname of the vice-captains using the code below but I can't return only those teams that include a captain or regular team members whose speciality is science. What would I need to add to do this?
part_two(Surname):-
team(_,player(_,Surname,_),_).
I've also been asked to also get the first name and the surname of any captain whose regular team members number more than one and who all have the same surnames.
This is my attempt so far:
part_three(First_name,Surname):-
team(Captain,_,Regular_team_members),
first_name(Captain,First_name),
surname(Captain,Surname),
Regular_team_members=[_,_|_].
I just need to exclude the details of those captains whose regular team members don't all have the same surname.

part_two(Surname):-
team(Captain, Vice_captain, Regular_team_members),
surname(Vice_captain, Surname),
member(Player, [Captain|Regular_team_members]),
specialty(Player, science).
% 'abstract data structures' accessors
surname(player(_First_name, Surname, _Details), Surname).
specialty(player(_First_name, _Surname, details(Speciality, _Recent_score)), Speciality).
Since you're going anyway to scan the Regular_team_members list, looking for appropriate constraint, you can get a simpler 'program' first 'joining' the Captain to other players.

You could change a little what you have already written as follows:
part_two(Surname):-
team(P,player(_,Surname,_),L),
( P=player(_,_,details(science,_)) -> true ; member(player(_,_,details(science,_)),L) ).
Example:
Database:
team(player(niall,elliott,details(history,11)),
player(michelle,cartwright,details(fashion,19)),
[player(peter,lawlor,details(history,12)),
player(louise,boyle,details(current_affairs,17))]).
team(player(niall1,elliott1,details(science,11)),
player(michelle1,cartwright1,details(fashion,19)),
[player(peter,lawlor,details(history,12)),
player(louise,boyle,details(current_affairs,17))]).
team(player(niall2,elliott2,details(history,11)),
player(michelle2,cartwright2,details(fashion,19)),
[player(peter,lawlor,details(science,12)),
player(louise,boyle,details(current_affairs,17))]).
team(player(niall3,elliott3,details(science,11)),
player(michelle3,cartwright3,details(fashion,19)),
[player(peter,lawlor,details(science,12)),
player(louise,boyle,details(current_affairs,17))]).
Now querying:
?- part_two(X).
X = cartwright1 ;
X = cartwright2 ;
X = cartwright3.

Related

Prolog Recursion | using multiple predicates to calculate sum

could someone tell me how do I solve the below question:
people_in_capitals(N): N is the total number of people living in capital cities of the world.
"Useful predicates:
country(Name, ID, Capital, CapitalProvince, Size, Population)
city(Name, Country ID, Province, Population, Lat, Lon, Elevation)."
I can use "findall" function to get and store the capitals in List, however, how do I use items in the list to find the population from the predicate city?
Show your code so far!
Also, write a predicate that iterates over the list (as described here: https://www.doc.gold.ac.uk/~mas02gw/prolog_tutorial/prologpages/lists.html) and sums the city population into the target value.

Prolog: can we pass a fact as argument to another fact?

I have a rule where I have to check whether a person who works for a department also works for its college and the university. The sample knowledge base is the following:
university(xxx). /*the university*/
col(scienceAndMath,xxx). /*the college of the university*/
dept_col(biology,scienceAndMath). /*the department of the college*/
dept_faculty(michael,biology). /*the faculty member of the department*/
I have to make a rule, namely works_for(X,Y) to check that an individual who works for a department, also works for its college and the university. I am trying to do this in the following manner, but to no avail.
works_for(X,Y):-dept_faculty(X,Y).
works_for(X,Y):-dept_col(works_for(X,Y),Y).
/*also tried works_for(X,Y):-dept_col(dept_faculty(X,Y),Y)./*
For example, for the query works_for(michael,X)., the result should be following:
X = biology
X = scienceAndMath
How do I pass a fact as argument to another fact? At present, the answer of the query is the following:
X = biology
false.

Prolog check for predicate category

I have been working with Prolog since today, and wanted to create a simple test case. The basic idea was to have multiple sports defined, and it looks as follows:
soccer :- category(ball_sport),
check(has_11_players_per_team),
check(large_ball),
check(use_feet).
tennis :- category(ball_sport),
...
category(ball_sport) :-
check(has_a_ball).
Now I wanted to create a testcase, to see if both sports are of the ball_sport category, but have no idea to check these sports against eachother .. I thought it would be something like the code below, but it's obvious not. Is there an easy way to check these predicate categories? Thanks
both_ballsports(sport_one, sport_two) :-
has_category(sport_one, ball_sport),
has_category_sport_two, ball_sport).
It seems that first of all, you want to declaratively state attributes of a sport.
For example:
sport_attributes(soccer, [ball_sport,players(22),ball(large),use(feet)]).
sport_attributes(tennis, [ball_sport,players(2),players(4),ball(small),use(racket)]).
Note that I am relating sports to attributes. For comparison, the goals of the form check(X) you use above all seem to lack a critical argument, namely the actual sport for which they hold (or not). For example, the goal check(use_feet) either holds or not, but there is no means to qualify a unary predicate of this kind and state different facts for different sports.
Note the naming convention: We describe what each argument means, separated by underscores.
With this representation, both_ballsports/2 could look like this:
both_ballsports(Sport1, Sport2) :-
ballsport(Sport1),
ballsport(Sport2).
ballsport(Sport) :-
sport_attributes(Sport, As),
member(ball(_), As).
Sample query and answer:
?- both_ballsports(Sport1, Sport2).
Sport1 = Sport2, Sport2 = soccer ;
Sport1 = soccer,
Sport2 = tennis ;
Sport1 = tennis,
Sport2 = soccer ;
Sport1 = Sport2, Sport2 = tennis ;
false.
This can be used in all directions!

What would you use for `n to n` relations in python?

after fiddling around with dictionaries, I came to the conclusion, that I would need a data structure that would allow me an n to n lookup. One example would be: A course can be visited by several students and each student can visit several courses.
What would be the most pythonic way to achieve this? It wont be more than 500 Students and 100 courses, to stay with the example. So I would like to avoid using a real database software.
Thanks!
Since your working set is small, I don't think it is a problem to just store the student IDs as lists in the Course class. Finding students in a class would be as simple as doing
course.studentIDs
To find courses a student is in, just iterate over the courses and find the ID:
studentIDToGet = "johnsmith001"
studentsCourses = list()
for course in courses:
if studentIDToGet in course.studentIDs:
studentsCourses.append(course.id)
There's other ways you could do it. You could have a dictionary of studentIDs mapped to courseIDs or two dictionaries that - one mapped studentIDs:courseIDs and another courseIDs:studentIDs - when updated, update each other.
The implementation I wrote out the code for would probably be the slowest, which is why I mentioned that your working set is small enough that it would not be a problem. The other implentations I mentioned but did not show the code for would require some more code to make them work that just aren't worth the effort.
It depends completely on what operations you want the structure to be able to carry out quickly.
If you want to be able to quickly look up properties related to both a course and a student, for example how many hours a student has spent on studies for a specific course, or what grade the student has in the course if he has finished it, and if he has finished it etc. a vector containing n*m elements is probably what you need, where n is the number of students and m is the number of courses.
If on the other hand the average number of courses a student has taken is much less than the total number of courses (which it probably is for a real case scenario), and you want to be able to quickly look up all the courses a student has taken, you probably want to use an array consisting of n lists, either linked lists, resizable vectors or similar – depending on if you want to be able to with the lists; maybe that is to quickly remove elements in the middle of the lists, or quickly access an element at a random location. If you both want to be able to quickly remove elements in the middle of the lists and have quick random access to list elements, then maybe some kind of tree structure would be the most suitable for you.
Most tree data structures carry out all basic operations in logarithmic time to the number of elements in the tree. Beware that some tree data structures have an amortized time on these operators that is linear to the number of elements in the tree, even though the average time for a randomly constructed tree would be logarithmic. A typical example of when this happens is if you use a binary search tree and build it up with increasingly large elements. Don't do that; scramble the elements before you use them to build up the tree in that case, or use a divide-and-conquer method and split the list in two parts and one pivot element and create the tree root with the pivot element, then recursively create trees from both the left part of the list and the right part of the list, these also using the divide-and-conquer method, and attach them to the root as the left child and the right child respectively.
I'm sorry, I don't know python so I don't know what data structures that are part of the language and which you have to create yourself.
I assume you want to index both the Students and Courses. Otherwise you can easily make a list of tuples to store all Student,Course combinations: [ (St1, Crs1), (St1, Crs2) .. (St2, Crs1) ... (Sti, Crsi) ... ] and then do a linear lookup everytime you need to. For upto 500 students this ain't bad either.
However if you'd like to have a quick lookup either way, there is no builtin data structure. You can simple use two dictionaries:
courses = { crs1: [ st1, st2, st3 ], crs2: [ st_i, st_j, st_k] ... }
students = { st1: [ crs1, crs2, crs3 ], st2: [ crs_i, crs_j, crs_k] ... }
For a given student s, looking up courses is now students[s]; and for a given course c, looking up students is courses[c].
For something simple like what you want to do, you could create a simple class with data members and methods to maintain them and keep them consistent with each other. For this problem two dictionaries would be needed. One keyed by student name (or id) that keeps track of the courses each is taking, and another that keeps track of which students are in each class.
defaultdicts from the 'collections' module could be used instead of plain dicts to make things more convenient. Here's what I mean:
from collections import defaultdict
class Enrollment(object):
def __init__(self):
self.students = defaultdict(set)
self.courses = defaultdict(set)
def clear(self):
self.students.clear()
self.courses.clear()
def enroll(self, student, course):
if student not in self.courses[course]:
self.students[student].add(course)
self.courses[course].add(student)
def drop(self, course, student):
if student in self.courses[course]:
self.students[student].remove(course)
self.courses[course].remove(student)
# remove student if they are not taking any other courses
if len(self.students[student]) == 0:
del self.students[student]
def display_course_enrollments(self):
print "Class Enrollments:"
for course in self.courses:
print ' course:', course,
print ' ', [student for student in self.courses[course]]
def display_student_enrollments(self):
print "Student Enrollments:"
for student in self.students:
print ' student', student,
print ' ', [course for course in self.students[student]]
if __name__=='__main__':
school = Enrollment()
school.enroll('john smith', 'biology 101')
school.enroll('mary brown', 'biology 101')
school.enroll('bob jones', 'calculus 202')
school.display_course_enrollments()
print
school.display_student_enrollments()
school.drop('biology 101', 'mary brown')
print
print 'After mary brown drops biology 101:'
print
school.display_course_enrollments()
print
school.display_student_enrollments()
Which when run produces the following output:
Class Enrollments:
course: calculus 202 ['bob jones']
course: biology 101 ['mary brown', 'john smith']
Student Enrollments:
student bob jones ['calculus 202']
student mary brown ['biology 101']
student john smith ['biology 101']
After mary brown drops biology 101:
Class Enrollments:
course: calculus 202 ['bob jones']
course: biology 101 ['john smith']
Student Enrollments:
student bob jones ['calculus 202']
student john smith ['biology 101']

Unique elements in list (Prolog)

I'm implementing a variation on Einstein's Riddle and i'm having some trouble.
When trying to calculate the solution i try this:
solve(Street) :- Street = [_House1,_House2,_House3,_House4,_House5],
%hint one goes here
%hint two goes here
%etc.
I can then ask the solution by typing: solve(Street)
However this comes up as solution:
house(flower, food, pet, sport)
house(flower, food, pet, sport)
house(x , food, pet, sport)
house(flower, food, pet, sport)
house(x, flower, pet, sport)
As you can see there's 2 times x, the rest are all types of foods, flowers, pets and sports.
But every type is unique: if one person likes flower X, noone else can like X.
Now, the reason why my solution gives 2 x's is easy to see: we are given an amount of hints but in all the hints there are only mentioned 4 flowers. So Prolog doesn't know there is another flower, and just uses x twice, just because it's possible and fulfills all the other hints.
What i want to say is that all the types of foods and flowers etc. in Street are unique so he should leave some blank when he used all types already. 3 would look like: house(x , food, pet ,sport) and 5 would look like: house(_, flower, pet, sport).
I also tried adding this to the hints: (let's say "cactus" is one of the flowers not mentioned in the hints)
member(house(cactus,_,_,_), Street)
However then my program doesn't end...
A hint may look like this:
is_neighbour(house(_,_,_,football),house(_,_,fish,_), Street),
with : is_neighbour(A,B,List) giving true when A and B are next to each other in List.
The hint can be translated to: the person who loves football lives next to the person who has fish.
If any more info need to be provided i'm willing to elaborate. :)
To express that no flower is reported twice, and also to make sure that all flowers are bound, you can use the permutation/2 predicate: the list of all flowers should be a permutation of the list of specified flowers. This would read like [untested]
flowers([], []).
flowers([house(Flower,_,_,_)|Street], [Flower|Rest]) :- flowers(Street, Rest).
-- ...
flowers(Street, Flowers),
permutation(Flowers, [kaktus, tulpe, nelke, rose, fingerhut]),
Edit: for 10 flowers, using permutations is probably too slow. An alternative approach is
flower(kaktus).
flower(tulpe).
flower(nelke).
--...
flowers(Street,[F1,F2,F3,F4,F5,F6,F7,F8,F9,F10]),
flower(F1), flower(F2), F1\=F2,
flower(F3), F3\=F1, F3\=F2,
flower(F4), F4\=F1, F4\=F2, F4\=F3,
--...

Resources