a challenging list question on prolog - list

I am new on prolog. Can you help me to solve this problem, please.
exam(math, paul).
exam(phys, paul).
exam(cmpe, sofia).
exam(bio, george).
I want to implement the predicate otherExam(L, N). L is a lesson, and N is the list of all lessons (except L) haved by student of L.
otherExam(math,X). returns [phys]
otherExam(cmpe,X). returns []
otherExam(chem,X). returns false (no such lesson)
otherExam(math,[phys]) returns true
otherExam(X,[phys]). returns math
I haved stucked with this problem. If you help me, I will be very happy :))

check findall/3 and select/3
findall/3 is used to get all the possible results from a query
for example, findall(X,ancestor(X,paul),L) will find all X such as X is an ancestor of paul and will put them in the list L

Related

Converting list of clauses to a query?

let say i have the following facts :
book(65).
own(named('Peter'), 65).
now got the query as a list of clauses :
[what(A), own(named('Peter'), A)]
or
[who(X), book(A), own(X, A)] .
how do I make a rule that accept this list and return the result. Keep in mind that the question could be Why,When,Who...
I went the usual way :
query_lst([]).
%% query_lst([what(Q)|T], Q) :- query_lst(T).
query_lst([H|T]) :- write('?- '),writeln(H),
call(H), query_lst(T).
but this does not allow binding of Q in wh(Q) to the answer which could be in any of the facts that are called by call()
Additional complication I did not forsee is that the query :
(what(A), own(named('Peter'), A).
would fail, because there is no what(X), fact in the DB.
I have to just bind somehow the variable A /that is in what()/ to query_lst(Goals,A) and of course remove what(X) from the list /which i can do with select/3 /
any idea how to bind list-Wh-var to query_lst result ?
my current solution (assumes Q is first element):
query_lst([G|Gs],Res) :- G =.. [Q,Res], member(Q,[what,why,who,when]), lst2conj(Gs,Conj), call(Conj).
Simply convert the list of goals into a conjunction and call it:
list_to_conjunction([], true).
list_to_conjunction([Goal| Goals], Conjunction) :-
list_to_conjunction(Goals, Goal, Conjunction).
list_to_conjunction([], Conjunction, Conjunction).
list_to_conjunction([Next| Goals], Goal, (Goal,Conjunction)) :-
list_to_conjunction(Goals, Next, Conjunction).
Then:
query_list(Goals) :-
list_to_conjunction(Goals, Conjunction),
call(Conjunction).
You got an answer, but it was an answer to your question, not to what you really wanted. Also, you edited your question after you accepted that answer, which isn't very helpful. Typically it's better to open a new question when you have... a new question.
Here is an answer to what you seem to want, which is not exactly what you asked. You have lists of the form [WhPart | Rest] where the WhPart is a wh-word with a variable, and the Rest is a list of goals. You want to execute these goals and get the variable in the wh-term bound.
The good news is that, since the variable in the wh-word also occurs in the goals, it will be bound if you execute them. No extra work is needed. Executing the goals is enough. If the wh-part is really at the start of the list, you can do the whole thing like this:
query([_WhPart | Body]) :-
call_body(Body).
call_body([]).
call_body([Goal | Goals]) :-
call(Goal),
call_body(Goals).
For example:
?- query([who(X), book(A), own(X, A)]).
X = named('Peter'),
A = 65.
?- query([what(A), own(named('Peter'), A)]).
A = 65.
As you can see, there is no need to convert the query to a conjunctive goal: Executing the queries in sequence is exactly the same as executing their conjunction.
Also, it doesn't actually matter which wh-word is used; the only thing that really matters is the variable contained within the term. For this reason the above version does no checking at all, and the _WhPart could be anything. If you want to check that it is a valid term, you can do the following:
query([WhPart | Body]) :-
wh(WhPart),
call_body(Body).
wh(who(_X)).
wh(what(_X)).
wh(when(_X)).
This buys you some "type checking":
?- query([foo(A), own(named('Peter'), A)]).
false.
But not a lot, since you don't know if the wh-word actually fits what is being asked:
?- query([when(A), own(named('Peter'), A)]).
A = 65.

prolog predicate returns facts(?)

First of all, if the answer to my question is here or here
I couldn't find it so please don't kill me.
I want to write a Prolog predicate which returns a list of this form:
(list[elem, elem2], list[elem3, elem4], list[elem5, elem6]).
now I can think of several ways to return a list of this form:
([elem, elem2], [elem3, elem4],[elem5, elem6]).
but how do I make the word "list" appear there as well? what is it even? a fact? another predicate?
Thanks in advance!
You can create a compound term using the standard =../3 built-in predicate. For example:
| ?- Term =.. [list, [1,2,3]].
Term = list([1,2,3])
yes
But note that the syntax that you're trying to use, list[elem5, elem6], is not valid. Are you trying to mimic an array representation? If so, maybe use instead list(elem5, elem6)? For example:
| ?- Term =.. [list, elem5, elem6].
Term = list(elem5, elem6)
yes

How to write a prolog predicate to trim first N elements from a List using conc (concatenation) operation

trim(L1,N,L2) which is true if L2 contains the first N elements of L1
I'm required to write the prolog code using the relation conc. Im new to prolog, so i have issue with my code. Can somebody correct me?
trim(L1, N, L2):- conc(L2,T,L1), length(L2,N),length(L1,N2), N2>= N
Most people have written the code using append and recurssion too. Please be kind enough to help me to use conc.
trim(L1,N,L2):-conc(L3,_,L1),conc(L2,_,L3),length(L2,N),!.
You are nearly there. Just make 'T' anonymous and you are good to go.
conc([],L,L).
conc([Head|L1],L2,[Head|L]):-conc(L1,L2,L).
trim(L1,N,L2) :-conc(L2,_,L1) ,length(L2,N).
And ask queries.
?- trim([1,2,3],1,X).
X = [1] ;
?- trim([1,2,3],0,X).
X = [] ;
?- trim([1,2,3],2,[1,2]).
true.

Prolog predicate doesn't resolve

This may be a rookie mistake, but I'm trying to solve this question:
Find the query to obtain the following answer using the findall predicate: Obtain a list of persons who work in a city other than the one where they live:
L = [suzy, paul].
This is the database:
city(ottawa,ontario).
city(toronto,ontario).
city(kingston,ontario).
city(gatineau,quebec).
city(montreal,quebec).
company(shopify,ottawa).
company(rossvideo,ottawa).
company(dium,gatineau).
company(uber,toronto).
company(deepmind,montreal).
company(google,toronto).
person(annie,gatineau).
person(paul,gatineau).
person(suzy,gatineau).
person(robert,gatineau).
person(tom,ottawa).
person(tim,kingston).
person(joe,montreal).
person(jane,ottawa).
person(marie,ottawa).
person(jack,toronto).
person(simon,toronto).
employee(annie,dium).
employee(tom,shopify).
employee(jane,shopify).
employee(marie,shopify).
employee(joe,deepmind).
employee(jack,google).
employee(simon,google).
employee(suzy,shopify).
employee(paul,rossvideo).
employee(marie,rossvideo).
employee(simon,uber).
Here is the predicate I tried to use to solve it:
worksIn(n, Y) :-
employee(n, Comp),
company(Comp, Y).
But it only returns false. Does anyone know how to fix it?
I did:
worksIn(P):- person(P,CL), employee(P, CO), company(CO, CW), CL/=CW.
so the final answer is:
findall(P, worksIn(P), L).
I'm not sure if we can add the "worksIn" thing in or not
when use variables, first letter must be uppercase.

Inserting value into the begining of each sublist

I'm currently writing a predicate that will run through a list of lists and insert a value I have calculated onto the beginning of the list
Step one is easy, just perform the calculation for each list and unify variable N with it.
checkthrough([]).
checkthrough([H|T]):-
count_validentries(H,N),
checkthrough(T).
What I'm trying to achieve now is to put that variable N onto the beginning of each of my sublists, so each list begins with the count of valid entries.
I have attempted to do this using an accumulator. Attempting to start with an empty list, and to every time add the new value N and the head of the list to it:
checkthrough([],Sofar,Lastone).
checkthrough([H|T],Sofar,Lastone):-
count_validentries(H,N),
Newsofar is [N,H|Sofar],
checkthrough(T,Newsofar,Lastone).
I'm quite sure I'm making a really stupid mistake somewhere along the lines. This is not valid Prolog syntax, failing with Arithmetic:' [2 internal variables]' is not a function.
Does anyone have any tips please?
Using meta-predicate maplist/3 and Prolog lambda simply write:
?- use_module(library(lambda)).
?- maplist(\Es^[N|Es]^count_validentries(Es,N), Ess, Xss).
Also, I'd guess that you're really looking for (-)/2 pairs which is how key-value pairs are commonly represented—by library predicates and the built-in predicate keysort/2. Consider:
?- Ess = [[a,b,c],[d,e],[],[f]],
maplist(\Es^(N-Es)^length(Es,N), Ess, Xss),
keysort(Xss, Yss).
Ess = [ [a,b,c], [d,e], [], [f]],
Xss = [3-[a,b,c], 2-[d,e], 0-[], 1-[f]],
Yss = [0-[], 1-[f], 2-[d,e], 3-[a,b,c]].
Maybe
checkthrough([],Sofar,Sofar).
checkthrough([H|T],Sofar,Lastone):-
count_validentries(H,N),
checkthrough(T,[[N|H]|Sofar],Lastone).
but you'll end up with the list reversed. Keeping it simpler will help
checkthrough([],[]).
checkthrough([H|T],[[N|H]|Rest]):-
count_validentries(H,N),
checkthrough(T,Rest).
or better, if you're running a recent version of SWI-Prolog:
checkthrough(L,L1) :-
maplist([E,E1]>>(count_validentries(E,N),E1=[N|E]), L,L1).