Rule and query Help in prolog - prolog

There are facts like :
student(ram, cs). // ram is student of cs branch
student(kiri,it).
student(akshay,cs).
student(sanjay,me).
I want to write a rule to find out the classmates in any branch AND a query to list out students in a branch say cs. Please help.
what query I had to run if i had to find classmates of akshay?

Two students are classmates if they are participating the same course.
classmates(X, Y) :- student(X, A), student(Y, A), X #< Y.
#</2 here is for suppressing duplicates. I.e it is enough to have only (A,B) without (B,A), (A,A) and (B,B).
?- classmates(X, Y).
X = akshay,
Y = ram ;
false.
To list out all students in a branch cs:
?- student(X, cs).
X = ram ;
X = akshay.

this being a follow up of this previous question, let's keep on the same mood...
classmates(Classmates) :-
aggregate(set(P), B^Q^(student(P,B), student(Q,B), P\=Q), Classmates).
yields
?- classmates(L).
L = [akshay, ram].

Related

Why does SWI-Prolog invent f/2 given only f/1?

SWI-Prolog 7.6.4:
?- dif(X, f(Y)), X=f(a).
X = f(a),
dif(f(f(a), Y), f(f(Y), a)).
Note that I use f/1 in the query, but the constraint is on f/2. It's not wrong, but seems rather circuitous. Why doesn't Prolog return
?- dif(X, f(Y)), X=f(a).
X = f(a),
dif(Y, a).
That f in the printed constraint has nothing to do with your f. It's just a placeholder to keep subterms together:
?- dif(X, incal(Y)), X=incal(a).
X = incal(a),
dif(f(incal(a), Y), f(incal(Y), a)). <--- residual constraints, not yet resolved
The above meaning just that:
incal(a) must stay different from incal(Y); and
Y must stay different from a
Yes, you could simplify that but ... when does one know whether optimization will cost less than one will gain?

Rule for amount of facts

For a school project I want to make a planning maker in Prolog. I want Prolog to make a planning for every day. On each day a certain amount of employees are needed and employees can only work on certain days. I want Prolog to make a planning that plans the right amount of people on each day. For that I wrote the following code:
workingday_employeesneeded(monday, 2).
workingday_employeesneeded(tuesday, 1).
workingday_employeesneeded(wednesday, 2).
employee_availability(tom, monday).
employee_availability(thomas, monday).
employee_availability(timme, monday).
employee_availability(timo, monday).
employee_availability(tom, tuesday).
planning(Employee, Day) :-
workingday_employeesneeded(Day, Amount),
employee_availability(Employee, Day).
planning(Employee, Day) :-
aggregate_all(count, planning(Employee, Day), Count),
workingday_employeesneeded(Day, Amount),
Count <= Amount.
However, I can't get Prolog to give me the right result, as I query the following Prolog gives me all the options, not regarding the amount of employees needed.
?- planning(X, Y).
X = tom,
Y = monday ;
X = thomas,
Y = monday ;
X = timme,
Y = monday ;
X = timo,
Y = monday ;
X = tom,
Y = tuesday ;
false.
Can you guy's see what I'm doing wrong? Thanks in advance!
EDIT:
I thought it might be handy to make a list of employees for each day in the planning. So I edited the code to the following (also fixing some syntax errors pointed out in the comments);
planning_on_day(Day, Employees) :-
workingday_employeesneeded(Day, Amount),
findall(E, employee_availability(E, Day), Employees),
length(Employees, Amount).
The following problem still exists; if there are more employees available than needed the program does not print the planning for that day instead of only picking the first N employees.
Do you guys have suggestions to fix that problem?
Simply your predicate fails because first you use findall/3 then you constraint the length of the list. For instance, for monday there are 4 employees available, you find all of them with findall/3 and store into Employees. Then you check the lenght of the list and it fails. To solve it you need find all the available employees and then find a subset of the list with desired length. So your code will be:
subset([], []).
subset([E|Tail], [E|NTail]):-
subset(Tail, NTail).
subset([_|Tail], NTail):-
subset(Tail, NTail).
planning_on_day(Day, Employees) :-
workingday_employeesneeded(Day, Amount),
findall(E, employee_availability(E, Day), E),
length(Employees,Amount),
subset(E,Employees).
?- planning_on_day(monday,P).
P = [tom, thomas]
P = [tom, timme]
P = [tom, timo]
P = [thomas, timme]
P = [thomas, timo]
P = [timme, timo]
false
?- planning_on_day(tuesday,P).
P = [tom]
false
?- planning_on_day(wednesday,P).
false
Then, if you want find the plan of the week you can add:
isDifferent(_, []).
isDifferent(X, [H | T]) :-
X \= H,
isDifferent(X, T).
allDifferent([]).
allDifferent([H | T]) :-
isDifferent(H, T),
allDifferent(T).
solve([],Plan,Plan):-
flatten(Plan,P),
allDifferent(P).
solve([Day|T],LT,Plan):-
workingday_employeesneeded(Day, Amount),
planning_on_day(Day,PlanD),
length(A,Amount),
subset(PlanD,A),
append(LT,[PlanD],LT1),
solve(T,LT1,Plan).
?- solve([monday,tuesday],[],L).
L = [[thomas, timme], [tom]]
L = [[thomas, timo], [tom]]
L = [[timme, timo], [tom]]

Prolog How to skip a fact

As the title says how can I accomplish this?
I have this knowledge base
person(alice).
person(mark).
person(sally).
likes(alice,coke).
likes(alice,fanta).
likes(alice,sprite).
likes(mark,water).
likes(mark,coffee).
likes(sally,pepsi).
And I want to output every drink that alice likes that is not fanta.Can someone explain how to achieve it and why it's done that way?
If you input the query likes(alice, X) you get all drinks that alice likes including fanta:
?- likes(alice, X).
X = coke ;
X = fanta ;
X = sprite.
If you add the goal X \= fanta in conjunction to that (note: must come after), then when X is bound to fanta, it will fail the goal X \= fanta, and it will backtrack to find other choices:
?- likes(alice, X), X \= fanta.
X = coke ;
X = sprite.
(note: pressing ; to get the next choice)
Suggested reading: Proof search

List indexes on a recursive program?

I've been searching for something that might help me with my problem all over the internet but I haven't been able to make any progress. I'm new to logic programming and English is not my first language so apologize for any mistake.
Basically I want to implement this prolog program: discord/3 which has arguments L1, L2 lists and P where P are the indexes of the lists where L1[P] != L2[P] (in Java). In case of different lengths, the not paired indexes just fail. Mode is (+,+,-) nondet.
I got down the basic case but I can't seem to wrap my head around on how to define P in the recursive call.
discord(_X,[],_Y) :-
fail.
discord([H1|T1],[H1|T2],Y) :-
???
discord(T1,T2,Z).
discord([_|T1],[_|T2],Y) :-
???
discord(T1,T2,Z).
The two clauses above are what I came up to but I have no idea on how to represent Y - and Z - so that the function actually remembers the length of the original list. I've been thinking about using nth/3 with eventually an assert but I'm not sure where to place them in the program.
I'm sure there has to be an easier solution although. Thanks in advance!
You can approach this in two ways. First, the more declarative way would be to enumerate the indexed elements of both lists with nth1/3 and use dif/2 to ensure that the two elements are different:
?- L1 = [a,b,c,d],
L2 = [x,b,y,d],
dif(X, Y),
nth1(P, L1, X),
nth1(P, L2, Y).
X = a, Y = x, P = 1 ;
X = c, Y = y, P = 3 ;
false.
You could also attempt to go through both list at the same time and keep a counter:
discord(L1, L2, P) :-
discord(L1, L2, 1, P).
discord([X|_], [Y|_], P, P) :-
dif(X, Y).
discord([_|Xs], [_|Ys], N, P) :-
succ(N, N1),
discord(Xs, Ys, N1, P).
Then, from the top level:
?- discord([a,b,c,d], [a,x,c,y], Ps).
Ps = 2 ;
Ps = 4 ;
false.

Prolog sum of numbers not in a list

In gnu Prolog I'm trying to collect the sum of all college credits in an College Adviser program.
Currently, it returns the number of credits like such:
| ?- totalCredits(joe, X).
X = 3 ? ;
X = 3 ? ;
X = 3 ? ;
X = 1 ? ;
X = 3 ? ;
X = 3 ? ;
no
And this is that code sample:
totalCredits(Student, Credits) :-
class(Class, _, _),
creditFor(Student, Class, _),
class(Class, _, Credits).
So my question is how can I sum up all of those results? I'm completely new to prolog and have only used functional languages before and so maybe I am missing something completely.
You can collect a list of credits and then sum it:
totalCredits(Student, Total) :-
findall(Credits, creditFor(Student, _Class, Credits), ListOfCredits),
sum_list(ListOfCredits, Total).
edit after comment, a correction: join the relations!
totalCredits(Student, Total) :-
findall(Credits,
(creditFor(Student, Class, _),
class(Class, _, Credits)), ListOfCredits),
sum_list(ListOfCredits, Total).
Use findall to get all the credits, then sum those.
findall(C, creditFor(Student, _, C), Credits),
sum(Credits, Sum).
(I hope I've understood the meaning of your creditFor predicate correctly.)
Here, sum is a summation predicate that I'll leave to you; it should be very similar to its functional counterpart.

Resources