Using cuts in prolog to select facts from database - prolog

I'm supposed to use Prolog cuts to get the first , the second and the last fact from the facts database , I found a way to get the first and the second but I can't find a solution for retrieving the last fact here is an example :
P(jack).
P(john).
P(alice).
P(sarah).
P(kyle).
Selecting the first fact only : first(X):-P(X),!.
Selecting the second fact only : second(Y):-P(X),P(Y),X\=Y,P(Y),!.
Selecting the last fact only : ?

I can't see a way without using negation, an accumulator, and the service predicate member, but since negation (by failure) is implemented with cuts, here is my bet:
last_(Y) :- collect([], [Y|_]).
collect(Seen, L) :-
p(X), \+ member(X, Seen), collect([X|Seen], L).
collect(All, All).
Instead of \+ member(Elem,List) (read Elem is not in List), you could implement a not_contains/2, with explicit cut inside.
BTW your second/1 predicate contains a redundant call: should be
second(Y):-p(X),p(Y),X\=Y,!.

Related

Accepting 2 different colors, but not the same colors

I'm trying out an exercise where I have to write the predicate, colors/2 (or colors(C1,C2) :- ...) that runs like the following:
?- colors(red,blue).
true.
?- colors(red,red).
false.
?- colors(blue,blue).
false.
So, essentially, I have to write my predicate in a manner where it doesn't accept when you enter the same color twice.
I am defining my facts to be:
col(red,blue).
col(purple,orange).
col(green, yellow).
I am making my predicate to be:
colors(X,Y) :- (col(X,Y); col(Y,X)) not (col(X,X); col(Y,Y)).
I don't understand why my predicate won't work. It is returning a syntax error with "Operator Expected." I am saying that it doesn't matter in what order you write the facts. Meaning, you can say colors(red,blue) or colors(blue,red), but you can't query colors with the same name without it returning false.
I would like to know:
Why this isn't a valid expression.
What I can do to fix the problem.
A couple of things:
You're missing a comma (,) before not and not/1 expects a single term in parentheses, so use more parentheses:
colors(X,Y) :- (col(X,Y); col(Y,X)), not( (col(X,X); col(Y,Y)) ).
As #PauloMora indicated, not/1 is deprecated in favor of the ISO \+/1, so better would be:
colors(X,Y) :- (col(X,Y); col(Y,X)), \+ (col(X,X); col(Y,Y)).
Then looking at col(X,X) and col(Y,Y), there are no facts or predicates where col(X,X) would be true (both arguments are the same). So each of these will always be false, and \+ (col(X,X); col(Y,Y)) will always be true. So the expression is superfluous, and your predicate becomes (at least with the pattern established in your current set of facts):
colors(X,Y) :- col(X,Y) ; col(Y,X).
Since you don't have any facts stipulated with matching colors (col(x,x)), then queries like col(red, red) will fail anyway.
Per the recommendation by #false, for an integrity check on equality of X and Y, the appropriate mechanism would be dif(X, Y):
colors(X, Y) :- (col(X, Y) ; col(Y, X)), dif(X, Y).
The parentheses are desired since , has higher precedence than ;. This would guard against the case where you happened to have a fact or predicate col/2 in which both arguments were the same (identical or unified).

Not in Prolog and use of Bagof

I have database like this:
movie(matrix,wachowski,thriller).
movie(terminator, cameron, thriller).
movie(Gladiator, scott, costume).
movie(star wars, lucas, fantasy).
movie(star trek, abrams, fantasy).
And I want to know who direct fantasy film except Abrams.
I suppose I need to use 'not' predicate, but I don't know exactly how it works.
?- movie(X,not(abrams),fantasy).
But unfortunately it doesn't work.
One more query is what kind of films is not a thriller:
?- movie(X,_,not(thriller)).
Still not working.
Next problem is I need to use predicate direct(Director, listsOfMovie) based on bagof.
?- direct(Director, listsOfMovie) :- bagof(Director,movie(Director,listsOfMovie,_), listsOfMovie).
Still without success :(
Anyone can help?
Use of not
You can't use Prolog predicates like functions. not/1 is a predicate which accepts a query as an argument. So this isn't doing what you think:
movie(X,not(abrams),fantasy).
This is querying movie with a second argument of not(abrams). You don't have any facts or predicates that match movie(_, not(_), _) so it will always fail.
If you want to know which films were not thrillers, you might render it:
movie(X, _, Type),
Type \= thriller.`
Using not, it might be:
not( movie(X, _, thriller) ).
If you wanted the syntax of movie(_, not(_), _) to work, you could write a predicate for it:
movie( Name, not(Director), Type ) :-
movie(Name, D, Type),
D \= Director.
Now we have either a fact or a predicate head that matches the form, movie(_, not(_), _), and then the query, movie(X, not(abrams), Y) would work. But it's not normally done this way.
Using bagof/3
Let's look at your use of bagof. In the simplest case, bagof is supposed to take three arguments:
bagof(X, {query involving X}, ListOfSatisfingXs)
So bagof will run the {query involving X} generating each X that makes it true, creating ListOfSatisfingXs, a unique, sorted list of such instantiations of X. In other words, ListOfSatisfingXs is the unique, sorted values of X that make {query involving X} succeed.
In your case, you've gotten the arguments to bagof a bit mixed up:
direct(Director, listsOfMovie) :-
bagof(Director, movie(Director, listsOfMovie, _), listsOfMovie).
Here, you're reusing your Director argument as your bagof argument, which is not good (since it's not intended). Since you're looking for a list of movies, the first argument should represent the movie. Your query to movie is using listsOfMovie, your intended target argument to hold the list result, which it shouldn't. And finally, listsOfMovie is an atom, not a variable, since it doesn't start with a capital letter.
The corrected version would be:
director_movies(Director, ListOfMovies) :-
bagof(Movie, movie(Director, Movie, _), ListOfMovies).
Here, the bagof is getting the *Unique, sorted list of Movie values such that movie(Director, Movie, _) is true and providing that resulting list in ListOfMovies.

how to write a recursive routine in prolog?

i have a to write a routine which lists all descendants so far i wrote
descend(X,Y) :- child(X,Y).
descend(X,Y) :- child(X,Z), descend(Z,Y).
which works fine so any descendent i need to find i just do descend(X,name). and it keeps giving me descendants of name in form of X= descend1, X = descend2
but to get the results i have to press ; every time what i am trying is to write is a routine descendb which gives the list of all descends without pressing ;
descendb(X) :- descend(A,X), write(A).
this is obviously wrong.
You can get all results with a 'failure driven' loop, aka forall/2
descendb(X) :- forall(descend(A,X), writeln(A)).
That's generally useful only when we have to do some 'side effect' on every solution found, like writeln (for instance) does.
Since you say you're after 'the list of all descends', try findall/3 instead:
descendb(X, Ds) :- findall(D, descend(D,X), Ds).
Since we have 2 arguments, you are not obliged to make a choice, descendb/1 and descendb/2 are effectively different predicates.

Prolog program with lists and sublists

Hi I have to solve a problem in Prolog, that sounds like this: deletes all the sublists of a list that are increasing. For example the list [1,[2],[3,4],6] becomes [1,6].
So far I have tried this but it's not working. Any help please ?
domains
el=integer
list=el*
element=integer;list
lista=element*
goal
elim([1,[2],[3],4)],L),
write(L).
predicates
elim(lista,lista)
is_increasing(lista)
is_list(lista)
clauses
is_increasing([A,B|T]) :-
B>A,
is_increasing([B|T]).
is_list([_|_]).
is_list([]).
elim([],[]).
elim([E|Es],[E|Ts]) :-
is_list(E),
is_increasing(E),
elim(Es, Ts).
attempt to modularize your code: first write an is_increasing/1. Since it appears that a list of 1 element is increasing, you can do as simply as
is_increasing([A,B|T]) :- B > A, is_increasing([B|T]).
is_increasing([_]).
then you can use it to discard elements while copying. Beware to check that an element is a list before calling. Here is a possible definition
is_list([_|_]).
is_list([]).
edit
there is a bad declaration, as advised by mbratch
element=i(integer);l(list)
should be
element=integer;list
Also, you forgot is_increasing([_])., and anyway you're not using at all is_list or is_increasing.
The rule eliminating sublists of course should read
elim([E|Es], Ts) :- is_list(E), is_increasing(E), elim(Es, Ts).
just add the base case and a copy. i.e. elim is a 3 clauses predicate...
edit apart the rule above, you need only a base case
elim([],[]).
and a copy
elim([E|Es],[E|Ts]) :- elim(Es, Ts).
just try to understand why the order of rules is also important in Prolog...

State facts with unbound variables

How would I state things "in general" about the facts? Suppose I need to state "everyone likes the person who likes him/her", and I have a list of people who may or may not like each other.
This is what I tried so far, but it's sure not the way to do it:
likes(dana, cody).
hates(bess, dana).
hates(cody, abby).
likes(first(Girl, OtherGirl), first(OtherGirl, Girl)).
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
because this won't even compile.
everybody([dana, cody, bess, abby]).
likes_reflexive(dana, cody).
hates(bess, dana).
hates(cody, abby).
likes_reflexive(X, Y):- likes(X, Y), likes(Y, X).
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
%% likes_reflikes_reflexive(X, Y):- likes(X, Y), likes(Y, X).
%% user:6: warning: discontiguous predicate likes_reflexive/2 - clause ignored
%% hates(Girhates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
%% user:8: warning: discontiguous predicate hates/2 - clause ignored
Unfortunately I don't understand what the warnings say. Hope it makes my intention more clear. I.e. by stating one fact, I also want to state the other related fact.
If you want to change your knowledge base dynamically, you can use asserts. If you want to modify existing predicate, you should define it as dynamic, e.g. :- dynamic(likes/2).. If predicate is undefined, you can omit it.
add_mutual_likes(X, Y) :- asserta(likes(X, Y)), asserta(likes(Y, X)).
:- initialization(add_mutual_likes(dana, cody)).
initialization/1 calls add_mutual_likes(data, cody) goal when file is loaded. add_mutual_likes/2 adds two facts to a database. asserta/1 converts it's argument into a clause and adds it to a database.
| ?- [my].
yes
| ?- listing(likes/2).
% file: user_input
likes(cody, dana).
likes(dana, cody).
yes
| ?- likes(cody, dana).
yes
| ?- likes(dana, cody).
yes
| ?- add_mutual_likes(oleg, semen).
yes
| ?- listing(likes/2).
% file: user_input
likes(semen, oleg).
likes(oleg, semen).
likes(cody, data).
likes(data, cody).
yes
I use gprolog.
Let's start with the warnings. They are merely "style" suggestions. They are telling you that all the definitions for likes and hates should be together. Trust me if you have a big Prolog program it becomes a nightmare to go around tour code to get the full definition of your predicate. It would be like writing half a function in C++ and finish it in another file.
Now, you want to say "everyone likes the person who likes him/her". I'm not sure why you are using that function "first" in the code. This would be sufficient:
likes(dana, cody).
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).
The second clause reads "Girl likes OtherGirl if OtherGirl likes Girl. This won't work.
If you ask your program "is it true that cody likes dana"
? likes(cody, dana)
Prolog will reason like this:
The answer is yes if dana likes cody (using the second clause).
Yes! Because dana likes cody (using the first clause).
This is not enough to make it a correct program. Since we are in Prolog you can say: "give me another solution" (usually by entering ";" in the prompt).
Prolog will think "I only used the first clause, I haven't tried the second".
The answer is Yes also if dana likes cody (using the second clause).
The answer is Yes according to the second clause, if cody likes dana.
But that's our initial query. Prolog will give you the same answer again and again, looping forever if you asked for all the solutions.
You can do two things here. The first is telling Prolog that one solution is enough. You do this adding a "!" (that basically says, clear all the open branches left to explore).
likes(dana, cody) :- !.
likes(Girl, OtherGirl) :- likes(OtherGirl, Girl).
Another alternative is to "stratify the program".
direct_likes(dana, cody).
likes(Girl, OtherGirl) :- direct_likes(OtherGirl, Girl), !.
likes(Girl, OtherGirl) :- direct_likes(Girl, OtherGirl).
What you want is a fact where Prolog does not care about the order of arguments. Alas, something like that does not exist. What you can do instead is define facts where the implied meaning is that it is valid for all argument orders (like_each in the example below). But of course, you cannot use these facts in that way. Instead, you define the actual predicate to try (hence the or ;) all possible argument orders.
Thus, the solution is:
%% bi-directional like
like_each(dana, cody).
likes(A, B) :- like_each(A, B); like_each(B, A).
%% optional: one-directional like
% likes(cody, sarah).
Also, be careful with
hates(Girl, OtherGirl):- \+ likes(Girl, OtherGirl).
If both variables are unbound (e.g., ?- hates(A,B)), it will always fail. This happens because Prolog first tries to find a match for likes, which always succeeds for two variables, and then negates the result. Thus, you cannot use hates to find all pairs who don't like each other.

Resources