Prolog query not returning the expected result - prolog

For university exam revision, I came across a past paper question with a Prolog database with the following structures:
% The structure of a media production team takes the form
% team(Producer, Core_team, Production_assistant).
% Core_team is an arbitrarily long list of staff structures,
% but excludes the staff structures for Producer and
% and Production_assistant.
% staff structures represent employees and take the form
% staff(Surname,Initial,file(Speciality,Grade,CV)).
% CV is an arbitrarily long list of titles of media productions.
team(staff(lyttleton,h,file(music,3,[my_music,best_tunes,showtime])),
[staff(garden,g,file(musical_comedy,2,[on_the_town,my_music])),
staff(crier,b,file(musical_comedy,2,[on_the_town,best_tunes]))],
staff(brooke-taylor,t,file(music,2,[my_music,best_tunes]))).
team(staff(wise,e,file(science,3,[horizon,frontiers,insight])),
[staff(morcambe,e,file(science,3,[horizon,leading_edge]))],
staff(o_connor,d,file(documentary,2,[horizon,insight]))).
team(staff(merton,p,file(variety,2,[showtime,dance,circus])),
[staff(smith,p,file(variety,1,[showtime,dance,circus,my_music])),
staff(hamilton,a,file(variety,1,[dance,best_tunes]))],
staff(steaffel,s,file(comedy,2,[comedians,my_music]))).
team(staff(chaplin,c,file(economics,3,[business_review,stock_show])),
[staff(keaton,b,file(documentary,3,[business_review,insight])),
staff(hardy,o,file(news,3,[news_report,stock_show,target,now])),
staff(laurel,s,file(economics,3,[news_report,stock_show,now]))],
staff(senate,m,file(news,3,[business_review]))).
One of the rules I have to write is the following:
Return the initial and surname of any producer whose team includes 2
employees whose CVs include a production entitled ‘Now’.
This is my solution:
recurseTeam([],0).
recurseTeam[staff(_,_file(_,_,CV))|List],Sum):-
member(now,CV),
recurseTeam(List,Rest),
Sum is Rest + 1.
query(Initial,Surname):-
team(staff(Surname,Initial,file(Speciality,Grade,CV)),Core_team,Production_assistant),
recurseTeam([staff(Surname,Initial,file(Speciality,Grade,CV)),Production_assistant|Core_team,Sum),
Sum >= 2.
The logic I have here is that I have a recursive predicate which takes each staff member in turn, and a match is found only if the CV list contains the production 'now', and as you can see it will return the Initial and Surname of a Producer if at least 2 employees CV contains the 'now' production.
So, at least as far as I can see, it should return the c,chaplin producer, right? Because this team has staff members who have CV's which contains the 'now' production.
But when I query it, e.g.
qii(Initial,Surname).
It returns 'false'.
When I remove the "member(now,CV)" predicate, it successfully returns all four producers. So it would seem the issues lies with this rule. Member is the built-in predicate for querying the contents of lists, and 'CV' is the list structure that is contained within the file structure of a staff structure.
Any ideas why this isn't working as I had expected?
Any suggestions on what else I could try here?

You need one more clause for the recurseTeam predicate, namely for the case that the first argument is a non-empty list, but its first element is a file structure that does not contain now.
In the current version, recurseTeam simply fails as soon as it encounters such an element in the list.
One possible solution is to add the following third clause for recurseTeam:
recurseTeam([staff(_,_,file(_,_,CV))|List],Sum):-
\+ member(now,CV),
recurseTeam(List,Sum).
Alternatively, one can use a cut ! in the second recurseTeam clause after member(now,CV) and drop \+ member(now,CV) in the third clause. This is more efficient, since it avoids calling member(now,CV) twice. (Note, however, that this is a red cut – the declarative and the operational semantics of the program are no longer the same. Language purists may find this disturbing – "real programmers" don't care.)

Related

Return values from database - Prolog

Good morning, I'm learning prolog.
But I already searched, but I didn't find a solution, it must be very simple, but I got stuck
I created a database like the following
data base([
movie(["Titanic"], [["Jack", "Dawson"] ,["Rose", "DeWitt", "Bukater"]], ["James","Cameron"] ),
movie(["Hulk"], [["Bruce", "Banner"] ,["Betty", "Ross"]], ["Ang","Lee"] )
]).
This database contains the name of the film, the name of the main characters and then the director.
The goal would be to create a function:
Example
find_movies_by_character("Bruce Banner")
Need return Titanic
I imagine something might be wrong, I'm open to new ideas
data base([ with that space in it, doesn't seem to be valid syntax in SWI Prolog. Wherever did you get that from?
This database contains the name of the film, the name of the main characters and then the director.
More specifically, the database contains a list containing movie terms, each containing a list containing the name of the film and (a list containing lists of parts of names of the main characters) and (a list containing parts of names of directors).
From how complicated it is to write out, it will be at least that complicated to work with. If you can simplify it, it will get easier to work with:
movie('Titanic', ['Jack Dawson', 'Rose DeWitt Bukater'], 'James Cameron').
movie('Hulk', ['Bruce Banner', 'Betty Ross'], 'Ang Lee').
find_movie_by_character(Character, MovieName) :-
movie(MovieName, Characters, _),
member(Character, Characters).
e.g.
?- find_movie_by_character('Bruce Banner', X).
X = 'Hulk'

How to extend prolog driver and determine number of columns and rows by reading an eps file

Good day! I would like to ask for help in understanding Prolog code as well as learning how to extend its functionality. Based on my limited yet newbie knowledge of programming, the function of the first given code (is quite like a main file), everything originates from it and thus it is the first file that is run before calling any other function.
main.pl
printSequences([]).
printSequences([Sequence|Sequences]):-
writeln(Sequence),
printSequences(Sequences).
loadHelpers:-
['helpers'],
['part01'],
['part02'],
['part03'],
['part04'].
part01:-
readExtremePegSolitaireFile('test04.eps',_,Game),
printGame(Game),
columnsAndRows(Game).
part02:-
readExtremePegSolitaireFile('part01test01.eps',_,Game),
printGame(Game),
openSpaces(Game).
part03:-
readExtremePegSolitaireFile('test04.single.eps',_,Game),
printGame(Game),
setof(Moves,fewestMoves(Game,Moves),AllMoves),
writeln(moves),
printSequences(AllMoves).
part04:-
readExtremePegSolitaireFile('test04.eps',_,Game),
printGame(Game),
noIslands(Game).
I don't think I have any problems understanding the first given code above, but my problem is mostly with this second given code and how to go about manipulating other files. I can't seem to understand the prefix part (is this the definition of a list of lists?) Also am I correct that most of the other functions are declared in this helper file to make the code more organized?
helpers.pl
:- module( helpers,
[ readExtremePegSolitaireFile/3
, printGame/1
]
).
prefix([H],[]).
prefix([H|T],[H|PreT]):-
prefix(T,PreT).
readExtremePegSolitaireFile(File,Moves,Game):-
open(File,read,Input),
read(Input,Moves),
readGame(Input,Temp),
prefix(Temp,Game),
close(Input).
readGame(Input,[]):-
at_end_of_stream(Input),
!.
readGame(Input,[Row|Rows]):-
\+ at_end_of_stream(Input),
read(Input,Row),
readGame(Input,Rows).
printGame(Game):-
writeln(game),
printRows(Game).
printRows([]).
printRows([Row|Rows]):-
writeln(Row),
printRows(Rows).
Last is a peg solitaire board that is given with the first line being the list of moves performed and the following lines are the board declarations (1,2,3,4 - players, x - peg, and '-' as empty spaces)
test04.eps
[r,d,u,r,l,l,l,d,l,u,r,r].
[2,-,x,x,x,x,x].
[x,x,-,x,-,x,x].
[x,3,x,-,x,-,x].
[x,-,4,x,x,x,x].
[x,x,-,x,x,-,x].
[x,x,x,1,-,x,x].
[x,x,x,x,x,x,x].
I would like to know how one would be able to calculate the number of columns and rows via a query columnsAndRows(Game). My first plan of action was to use something like this: (Which would be able to calculate the length of the rows by counting each element in the list however, it seems to have calculated all the elements in the list. 2 things that I noticed was:
It didn't stop at the end of the row
Apparently it didn't print the entire board, it was missing the last line of the board!
columnsAndRows(Game) :-
flatten(Game, FlatList),
length(FlatList,FlatListSize),
writeln(FlatListSize).
?- [a04tests].
?- loadHelpers.
?- part01.
game
[2,-,x,x,x,x,x]
[x,x,-,x,-,x,x]
[x,3,x,-,x,-,x]
[x,-,4,x,x,x,x]
[x,x,-,x,x,-,x]
[x,x,x,1,-,x,x]
42
true
I'm honestly really lost and I'd appreciate any guidance as to where to begin, or even a process flow for this program. Many thanks!

findall(X,condition,List) list is filled with pointers and not real objects

I am trying to get a list full of objects from the database that fit my condition.
Here is my database:
student(1234,[statistics/a,algorithms/b,prolog/a,infi/b]).
student(4321,[java/a,algorithms/a,prolog/b,probability/b,infi/a]).
student(1111,[algorithms/a,prolog/a,c/a,infi/a]).
student(2222,[c/b,algorithms/b,prolog/b,infi/a]).
student(3333,[java/b,algorithms/b,prolog/a,infi/a]).
student(4444,[java/a,c/a,prolog/a]).
student(5555,[java/a,c/a,prolog/b,infi/b]).
student(6666,[java/a,c/a,prolog/b,infi/b,probability/b,algorithms/b]).
I wrote a predicat that queries which student has a string in the list attached to him that has: "infi/a"
findall(Ns,(student(Id,List),subset([infi/a],List)),L1)
The problem is that L1 does not return me a list as the following:
L1 = [student(2222,[c/b,algorithms/b,prolog/b,infi/a]),
student(1111,[algorithms/a,prolog/a,c/a,infi/a]) etc...]
It returns:
L1 = [_G2044, _G2041, _G2038, _G2035].
Why does this happen and how can I fix this?
You probably need to look up the specifications of the findall/3 predicate the first parameters is the Template (or yield): it specifies what you want to put into the list.
So you should not simply write X, but a term in which you are interested. For instance:
findall(Id,(student(Id,List),subset([infi/a],List)),L1).
will generate all the Ids of the students with infi/a in their list of courses; or if you are interested in the student/2 objects, you can write:
findall(student(Id,List),(student(Id,List),subset([infi/a],List)),L1).
So typically you work with the variable(s) you specify in the Goal (the second parameter), in case there is a free variable in your yield, it will make new free variables.

Prolog list issue

I have the following rules:
/*The structure of a subject teaching team takes the form:
team(Subject, Leader, Non_management_staff, Deputy).
Non_management_staff is a (possibly empty) list of teacher
structures and excludes the teacher structures for Leader and
Deputy.
teacher structures take the form:
teacher(Surname, Initial,
profile(Years_teaching,Second_subject,Club_supervision)).
Assume that each teacher has his or her team's Subject as their
main subject.*/
team(computer_science,teacher(may,j,profile(20,ict,model_railways)),
[teacher(clarke,j,profile(32,ict,car_maintenance))],
teacher(hamm,p,profile(11,ict,science_club))).
team(maths,teacher(vorderly,c,profile(25,computer_science,chess)),
[teacher(o_connell,d,profile(10,music,orchestra)),
teacher(brankin,p,profile(20,home_economics,cookery_club))],
teacher(lynas,d,profile(10,pe,football))).
team(english,teacher(brewster,f,profile(30,french,french_society)),
[ ],
teacher(flaxman,j,profile(35,drama,debating_society))).
team(art,teacher(lawless,m,profile(20,english,film_club)),
[teacher(walker,k,profile(25,english,debating_society)),
teacher(brankin,i,profile(20,home_economics,writing)),
teacher(boyson,r,profile(30,english,writing))],
teacher(carthy,m,profile(20,music,orchestra))).
I am supposed to bring back the initial and surname of any leader in a team that contains a total of 2 or more teachers with ict as their second subject.
I am new to prolog so unsure of this. Also, I have gotten back the results correctly but it is being returned 3 times.
Any help on this would be greatly appreciated.
Also, my aplogies if this is terribly easy.
You didn't provide the code you use to find these teachers, so I can't say for sure, but if there were a team with 3 members w/ ict as their second subject (for example, computer_science), then there would be 3 ways to find 2 (AB, AC, and BC), which would explain your multiple results. But saying how to modify your code to fix that would require seeing the code to be fixed.

Efficient algorithm for association Obj1 to Obj2 based on a rule set

I have a table containing millions of transaction records(Obj1) which looks like this
TransactionNum Country ZipCode State TransactionAmount
1 USA 94002 CA 1000
2 USA 00023 FL 1000
I have another table containing Salesreps records(Obj2),again in hundreds of thousands.
SalesrepId PersonNumber Name
Srp001 123 Rohan
Srp002 124 Shetty
I have a few ruleset tables,where basically rules are defined as below
Rule Name : Rule 1
Qualifying criteria : Country = "USA" and (ZipCode = 94002 or State = "FL")
Credit receiving salesreps :
Srp001 gets 70%
Srp002 gets 30%
The qualifying criteria is for the transactions,which means if the transaction attributes match the criteria in the Rule then credits are assigned to the salesreps defined in the rule's credit receiver section.
Now,I need an algorithm which populates a result table as below
ResultId TransactionNumber SalesrepId Credit
1 1 Srp001 700
2 2 Srp002 300
What is the efficient algorithm to do this?
So your real problem is how to quickly match transactions to potential rules. You can do this with an inverted index that says which rules match particular values for the attributes. For example, let's say you have these three rules:
Rule 1: if Country = "USA" and State = "FL"
S1 gets 100%
Rule 2: if Country = "USA" and (State = "CO" or ZIP = 78640)
S2 gets 60%
S3 gets 40%
Rule 3: if Country = "UK"
S3 gets 70%
S2 gets 30%
Now, you process your rules and create output like this:
Country,USA,Rule1
State,FL,Rule1
Country,USA,Rule2
State,CO,Rule2
ZIP,78640,Rule2
Country,UK,Rule3
You then process that output (or you can do it while you're processing the rules) and build three tables. One maps Country values to rules, one maps State values to rules, and one maps ZIP values to rules. You end up with something like:
Countries:
USA, {Rule1, Rule2}
UK, {Rule3}
States:
FL, {Rule1}
CO, {Rule2}
"*", {Rule3}
ZIP:
78640, {Rule2}
"*", {Rule1, Rule3}
The "*" value is a "don't care," which will match all rules that don't specifically mention that field. Whether this is required depends on how you've structured your rules.
The above indexes are constructed whenever your rules change. With 4000 rules, it shouldn't take any time at all, and the list size shouldn't be very large.
Now, given a transaction that has a Country value of "USA", you can look in the Countries table to find all the rules that mention that country. Call that list Country_Rules. Do the same thing for States and ZIP codes.
You can then do a list intersection. That is, build another list called Country_And_State_Rules that contains only those rules that exist in both the Country_Rules and State_Rules lists. That will typically be a small set of possible rules. You could then go through them one-by-one, testing country, state, and ZIP code, as required.
What you're building is essentially a search engine for rules. It should allow you to narrow the candidates from 4,000 to just a handful very quickly.
There are a few problems that you'll have to solve. Having conditional logic ("OR"), complicates things a little bit, but it's not intractable. Also, you have to determine how to handle ambiguity (what if two rules match?). Or, if no rules match the particular Country and State, then you have to back up and check for rules that only match the Country ... or only match the State. That's where the "don't care" comes in.
If your rules are sufficiently unambiguous, then in the vast majority of cases you should be able to pick the relevant rule very quickly. Some few cases will require you to search many different rules for some transactions. But those cases should be pretty rare. If they're frequent, then you need to consider re-examining your rule set.
Once you know which rule applies to a particular transaction, you can easily look up which salesperson gets how much, since the proportions are stored with the rules.

Resources