setof with compound predicate - prolog

I am struggling with a question in an assignment with prolog.
So, I have the following fact database in prolog:
student(name(doe, [john]), 33332, ['CSI1120'] ).
student(name(doe, [jane]), 33336, ['CSI1120'] ).
evaluation('CSI1120', homework(1), ['Prolog', database ], 5).
mark('CSI1120', 33332, homework(1), 3.5 ).
mark('CSI1120', 33336, homework(1), 4.0 ).
My goal here is to create a predicate listAllMarks/3 such as
?- returnAllMarks('CSI1120',homework(1),L).
Returns:
L= [(33332, 3.5), (33336, 4.0)].
In order to resolve this problem, I was thinking of making use of the prolog setof/3, so I came with the following predicate.
returnAllMarks(C,H,L):- setof(L,mark(C,_,H,X),[L|X]).
This doesn't seems to work, the predicate always return false. I"m suspecting that this may be due to the fact I am using setof against a compound predicate, but I could be wrong (I'm still in the early stages of learning prolog).
Do you guys have an idea? I look at this problem from all angles, and I am stuck here.
Thank you.

You could write something like:
returnAllMarks(C,H,L):- setof( (X,Y), mark(C,X,H,Y), L).
Example:
?- returnAllMarks('CSI1120',homework(1),L).
L = [ (33332, 3.5), (33336, 4.0)].

Related

forall/2 predicate query doesn’t return results

I have the following facts.
loves(ami, paul).
loves(lucy, paul).
female(ami).
artist(ami).
female(lucy).
artist(lucy).
canadian(paul).
lovesCanadianArtists(Person) :- forall(canadian(X), loves(Person, X)).
When I execute the query in SWI-Prolog:
?- lovesCanadianArtists(X).
The answer is true, and I don't get results.
Someone told me that the issue is the predicate isLovedByArtists(Person) is not inversible or invertible. So, I should add a condition on Person variable because it is not bound by the forall\2 predicate. Like:
lovesCanadianArtists(Person) :- female(Person), forall(canadian(X), loves(Person, X)).
So, my questions are:
Is this predicate invertibility documented anywhere ? I can't find it.
For me, the explanation given is wrong, and but I am not sure whether I should get results with my first rule. What's the underlying issue here ?
What's the underlying issue here ?
"forall/2 does not change any variable bindings. [...] If your intent is to create variable bindings, the forall/2 control structure is inadequate." - https://www.swi-prolog.org/pldoc/man?section=forall2
Perhaps one of foreach, findall, bagof or setof will do what you want, although it's not clear from the plural 'artists' and singular 'Person' exactly what that is; e.g. for a list of all People who love at least one Canadian artist, which may have duplicates if you add more Canadian artists:
lovesCanadianArtists(People) :-
findall(Person, (canadian(X), loves(Person, X)), People).

Sorting a knowledge database in Prolog

It's my first day with Prolog, and i need some help.
I have a Konwledge database of students student(Id,fName,lName,grade), I would like to display the list of students in order of merit, I have some ideas but I can not realise theme since I'm not familiar at all with prolog programming paradigm and syntax.
please recommend me a code that do this.
student(1, sleve, mcdichael,4).
student(2, darryl, archideld,2).
student(3, mario, mcrlwain, 1).
student(4, bobson, dugnutt, 3).
student(5, dean, wesrey, 6).
student(6, mike, truk, 5).
student(7, dwigt, rortugal, 7).
sortedStudents(SortedStudents) :-
findall(Grade-First-Last,
student(_, First, Last, Grade),
StudentData),
sort(1, #=<, StudentData, SortedStudents).
Uses findall/3 to get all the student records and turn them into a compound term of grade-firstname-lastname and sort/4 (might be SWI Prolog specific) to sort by term entry 1 (grade):
e.g.
?- sortedStudents(S).
S = [
1-mario-mcrlwain,
2-darryl-archideld,
3-bobson-dugnutt,
4-sleve-mcdichael,
5-mike-truk,
6-dean-wesrey,
7-dwigt-rortugal
]

Prolog Cut operator

I defined my knowledge base as:
edge(mammal,isa,animal).
edge(human,isa,mammal).
edge(simba,isa,human).
edge(animal,swim,bybirth).
edge(human,swim,mustlearn).
path(X,Y) :- edge(X,isa,Y).
path(X,Y) :- edge(X,isa,Z), path(Z,Y).
swim(X,Y) :- edge(X,swim,Y).
swim(X,Y) :- path(X,Z), swim(Z,Y).
Now, to use the above knowledge base, I use the following:
?- swim(simba,bybirth).
?- swim(simba,mustlearn).
And for both the queries, Prolog returns true. I want Prolog to check for the property swim locally first, then look at the direct parent, and so on in a hierarchical fashion. And it should stop searching as soon as we know that Simba "mustlearn" to swim, and shouldn't look any further. Thus, it should return false for the first query and true for the second.
I know it has to be done by limiting backtracking. I tried using the cut and not operators, but couldn't succeed. Is there a way to achieve this?
I tried it and ran into a problem too. I thought this might work:
swim(X,Y) :- once((edge(X,swim,Y); path(X,Z), swim(Z,Y))).
It doesn't work, because if Y is already instantiated on the way in, the first step will fail to unify and it will try the second route going through the human intermediate. So even though the query only produces one result, it can be fooled into producing swim(simba, bybirth). The solution is to force Prolog to commit to a binding on another variable and then check that binding after the commitment:
swim(X,Y) :-
once((edge(X,swim,Method); path(X,Z), swim(Z,Method))),
Method = Y.
This tells Prolog, there is only one way to get to this method, so find that method, and then it must be Y. If you find the wrong method, it won't go on a search, it will just fail. Try it!

What is the meaning of predicate "simple/1" in Prolog (SWI-Prolog)

I run into problem while reading a book.
I see a program use predicate "simple" ( I guess simple/1 ). I don't know what is the meaning of this predicate, I can't find it with ?-help(simple) in the console.
But when I tried with some queries in console, it worked something like:
5 ?- simple(p(x)).
false.
6 ?- simple(mia).
true.
7 ?- simple(Mia).
true.
8 ?- simple(f(Mia)).
false.
I guess it is some sort of predicate to determine if the argument was Terms(or Variables) or Complex Terms.
The swi-prolog manual has the following definition:
simple(#Term) is semidet
Term is atomic or a variable.
the definition is in the quintus prolog compatibility library; in the quintus prolog documentation the definition is:
simple(+Term)
Term is currently instantiated to either an atom, a number, a database
or a variable.
in any case, simple/1 is true if the argument is a simple term (not sure what the quintus manuals means by database; possibly a handler for an ODBC connection i guess)
translated to ISO predicates:
simple(T) :- var(T) ; atomic(T).
var/1 it's the most basic metaprogramming device, because it's impossible to predicate (i.e. execute code, binding variables) about any clause without instancing the variables, that are many times the essential part we are interested to.

SWI-Prolog How Prolog handles logical comparisons

I have written the following code in SWI-Prolog:
:- dynamic state_a/1 .
:- dynamic state_b/1 .
:- dynamic state_c/1 .
state_a([1,2,3,4,5,0]).
state_b([0]).
chop(LIST,HEAD,TAIL) :- LIST=[HEAD|TAIL].
move_ab :- !,state_a(X),
chop(X,Ha,Ba),
Ha>0,
state_b(Y),
chop(Y,Hb,Bb),!,
(Ha<Hb ; Hb =:= 0),
asserta(state_a(Ba)),asserta(state_b([Ha|Y])),
retract(state_a(X)), retract(state_b(Y));
write('Wrong Move.Choose Another').
There are 2 OR(;) in my code. When I ask move_ab in Prolog for the first time all the conditions before the second OR are true so i get the answer true from Prolog.
But the second time I ask move_ab in Prolog I get just the answer false.
I don't know how this can happen. Some of the conditions before the second OR are not true so Prolog should check the condition after the second OR and write me the message Wrong Move.Choose Another..
I tried using () to group the conditions but I still get the same message.
Any idea about what is going on? By the way I am a newb in Prolog, just started 2 days ago :)
The problem lies in the use of cut (!/0) before the conditions are properly tested. Cut removes choice points. Here it means that the choices are removed before you even test anything. So if the tests fail, everything fails !
BTW, manipulating the database is maybe not the best idea there. To represent states, you could use global variables as follows :
:- nb_setval(state_a, [1,2,3,4,5,0]).
:- nb_setval(state_b, [0]).
move_ab :-
nb_getval(state_a, [Ha|Ta]),
Ha > 0,
nb_getval(state_b, [Hb|Tb]),
(Ha < Hb ; Hb =:= 0),
nb_setval(state_a, Ta),
nb_setval(state_b, [Ha, Hb|Tb]),
!
;
write('Wrong Move.Choose Another').
A general advice given to beginners in prolog is to stay away from database manipulation if possible, as often problems are solvable without it. Here though it could be justified, but global variables will be faster and easier to manipulate.

Resources