PROLOG: Trying to get around predsort deleting duplicate objects - sorting

I'm trying to make it so predsort wont delete any duplicate objects that I have, and I think I know the logic in it but I can't seem to get it to work. I've been trying to call previous predicates that sort by another item when a duplicate is found but im getting errors when I try this.
process(3, X) :-
nl,
sort(X, Q),
show_records(Q),
nl, nl, menu(Q).
process(4, X) :-
nl,
predsort(sortName,X,Q),
show_records(Q),
nl, nl, menu(Q).
sortName(T, [_,E,_], [_,R,_]) :-
(compare(T, E, R)
->process(3, X)
).
process(5, X) :-
nl,
predsort(sortGrade,X,Q),
show_records(Q),
nl, nl, menu(Q).
sortGrade(T, [_,_,E], [_,_,R]) :-
(compare(T, E, R)
->process(4,X)
).
Process 3 sorts by the first value in the sublist, 4 the second one, and 5 the last. I'm trying to make it where when 5 finds a duplicate it'll go to 4, and from 4 to three if need be.

I will answer anyway, even if you spotted you're own solution, since it's not clear to me if ditto solution matches with mine.
What I mean: time ago I wrote my own predmsort/3 (predating in obvious mode the terminoloy of sort/msort) for exactly the same reason you have reported.
Time passed before I realized that there was a much better solution to my problem:
just never return =.
That is, so simple...

Related

Use a rule if a fact doesn't exist in prolog?

I'm new to prolog.
The idea of my project is to say "The room X is free if none is a guest of X, while X is taken if a family lives in X".
I use a predicate
guest(FamilySurname,RoomTaken)
So this mean that a family has taken that room.
taken(X) :- guest(_,X).
So if a family lives in room X, then X is taken.
My problem is how could i say that the room X is free? I should use a kind of NOT, like:
free(X) :- "NOT EXIST" guest(_,X).
How could i translate that "NOT EXIST" in prolog?
I have tried with ! but it doesn't work properly... maybe I'm placing it in the wrong way.
Sorry for my english.
Check the following code:
taken(X, Y) :- guest(Y,X).
free(X) :- \+ guest(_,X).
I made the a little change to taken, now it shows you who is on that room.
guest(albert, 123).
?- taken(123, R).
R = albert.
And the free() predicate it's pretty straightforward, I used the negation operator, you can check How to negate in Prolog
The code in the first answer does not seem to be a solution, i.e. the query ?- free(X) will not give an answer, where as the query ?- free(foo) will be successful. One needs to consider query floundering in Prolog into account, i.e. all variables
Consider the following code in which two alternatives for presenting the free predicate.
room(123).
room(124).
guest(albert, 123).
taken(Room) :- guest(_, Room).
free2(Room) :- room(Room),
\+ taken(Room).
free(Room) :- room(Room),
forall(guest(_, RoomTaken),
RoomTaken \= Room).

Six seats at the table

I'd like to limit the number of people sitting at a table (6 to be precise) but with prolog I can't figure out how to do it
I have already tried to use between (0, 5, W),
but I don't understand why every time with the active trace I see that it advances with the increase of W but it fails to exit the next function
(W =: = 0 -> nb_setval (counter, 0), W is 5,
person(antonella).
person(domenico).
person(raffaella).
person(tommaso).
person(vincenzo).
person(azzurra).
person(cristiano).
person(francesca).
person(luigi).
person(giovanni).
person(marcella).
person(daniela).
person(nunzio).
person(leonardo).
person(silvia).
notinfamily(giovanni).
notinfamily(marcella).
notinfamily(daniela).
notinfamily(nunzio).
notinfamily(leonardo).
%declare fight
fight(giovanni, marcella).
fight(marcella, daniela).
fight(luigi, leonardo).
%declare the familys
family(antonella).
family(domenico).
family(raffaella).
family(tommaso).
family(vincenzo).
family1(azzurra).
family1(cristiano).
family1(francesca).
family1(luigi).
sitdown :-
( person(X);family(X)),
\+ fight(X,_),
\+ fight(_,X),
between(0, 5, W),
( W =:= 0 -> nb_setval(counter,0), W is 5,
writeln(W),
nb_getval(counter,Countval),
writeln(Countval)).
I expect the function to continue after the lf (which in prolog should be "->") but it always remains there without going ahead. I can insert what the Trace returns to me.
I have an incomplete solution. But I hope it is enough to give you some hope to figure out the rest. First I revisited your data model slightly:
family(antonella,1).
family(domenico,1).
family(raffaella,1).
family(tommaso,1).
family(vincenzo,1).
family(azzurra,2).
family(cristiano,2).
family(francesca,2).
family(luigi,2).
Also I removed notinfamily/1 since you had one person who wasn't in a family or notinfamily and it cost me some debugging time. :) Better to infer it.
Next we need a couple helpers. First some helpers for your data model to make it easier to determine if families match or if people are fighting. These should be fairly straightforward to understand, I'm just ensuring that either the families actually match or one of them is not in a family (which we check with negation rather than notinfamily/1):
family_matches(P1, P2) :-
family(P1, Family),
family(P2, Family).
family_matches(P1, P2) :-
\+ family(P1, _) ; \+ family(P2, _).
Fighting is not currently associative, so I made a separate predicate that is associative:
fighting(X, Y) :- fight(X, Y) ; fight(Y, X).
Initially I was using select/3 which is really a nice way of plucking various items out of a list, but I realized doing different permutations was killing me, so I came up with this version that I can use to only generate combinations:
select_inorder(X, [X|Xs], Xs).
select_inorder(X, [_|Rem], Xs) :-
select_inorder(X, Rem, Xs).
The use of this predicate will become clear in just a second.
I prefer not to get arithmetic involved in Prolog if I can avoid it. This seems at first blush like a case where you can just assume that there will be three tables of five and everything will be great. That turns out not to be true, but I thought I'd share the code I worked up anyway in case it helps you solve the problem. My plan was this: I'll have a predicate that knows it's assembling a list of five people. I'll initially turn your database into a list of people. I'll choose people from that list. When I have five, I'll verify that the table is valid. I'll have a main predicate that does the book keeping and runs this table generator three times.
seating([T1,T2,T3]) :-
findall(X, person(X), People),
make_table(People, T1, R1),
make_table(R1, T2, R2),
make_table(R2, T3, []).
This could probably be restated with DCG syntax more clearly. But that should look sensible to you. Making a table has a similar flavor:
make_table(People, [A,B,C,D,E], Unseated) :-
select_inorder(A, People, P1),
select_inorder(B, P1, P2),
select_inorder(C, P2, P3),
select_inorder(D, P3, P4),
select_inorder(E, P4, Unseated),
safe_table([A,B,C,D,E]).
select_inorder/3 is used here to make sure that we choose people in order, since otherwise we're just wasting processing time. Once we have five people, we verify it. This is a classic "generate-test" loop.
safe_table(Table) :-
forall((member(L, Table), member(R, Table)),
(family_matches(L, R), \+ fighting(L, R))).
I kind of like the logical reading here. This is not super efficient, it's going to do a cross-product of all the items in the list. However, I think such a thing may be warranted since family_matches is not transitive (family_matches(antonella, giovanni), family_matches(giovanni, azzurra) is true but family_matches(antonella, azzurra) is not true).
Now of course, the bummer is that this does not work. But the reason it does not work is pretty simple: after we have placed all of family 1 at one table and all of family 2 at another table we have one person to move to their table and then we have to find a combination of five of the remaining people to sit together. But unfortunately, three of the people not in a family are fighting with each other. If you can make marcella and daniela get along, you'll get exactly one solution:
?- seating([T1,T2,T3]).
T1 = [antonella, domenico, raffaella, tommaso, vincenzo],
T2 = [azzurra, cristiano, francesca, luigi, giovanni],
T3 = [marcella, daniela, nunzio, leonardo, silvia] ;
false.
Making this work for incomplete tables sounds a bit tedious to me so I'm going to leave this where it is in the hopes it's still helpful to you somehow.

Need a full list of output

Below are the codes I came up with. However, there are two problems here.
First, only first value of X is displayed which is 1. Second, if Y is false, it doesn't jump back to menu1.
Hope to get some help in this.
time(office_hour,1).
time(office_hour,2).
menu1 :-
write('Input time'),nl,
read(Y),nl,time(Y,X),nl,write(X),nl,menu1.
This is the example of the scenario. As seen below, only value 1 is shown. How to display value 2 too? Sry I'm just a newbie
?-menu1.
Input time
office_hour.
1.
Input time
You need to allow Prolog to backtrack to get all of the solutions. The problem with your predicate is that it does a recursive call before it can backtrack. Also, you continue to recurse one level deeper for the next menu prompt which is probably not good practice since it continually consumes more stack as it responds to user inputs.
Here's another approach, using the repeat/0 predicate along with fail/0. repeat/0 simply succeeds which allows to re-query for solutions during backtracking. And fail/0 always fails, which forces backtracking. It's a common way to do a continual repeat loop and works well for looping on a menu. I've also adjusted the nl/0 usage a bit to make the output a little neater, and I renamed time/2 since it is just too generic a name and could potentially collide with a system predicate.
item_time(office_hour,1).
item_time(office_hour,2).
menu1 :-
repeat,
nl, write('Input time'), nl,
read(Y),
item_time(Y, X),
write(X), nl,
fail.
This will yield:
| ?- menu1.
Input time
office_hour.
1
2
Input time
foo.
Input time
...
What happens here is that fail/0 will cause the predicate clause to backtrack through the write(X), nl and to the time(Y, X) which will come up with the alternate solutions, succeed on those solutions, and then move forward to the write(X), nl again. If time(Y, X) finds no more solutions, then it finally fails and the clause backtracks all the way back to the repeat/0 call, which always succeeds. That results in the execution moving forward again to the nl, write('Input time'), ....
As you can see, inputting something unrecognized just re-prompts. To make this more complete, you could add atoms that are recognized as a "quite" and you could write a diagnostic message if you get input that doesn't exist in your database. That would be a good exercise for learning.
The second problem:
menu1 :-
write('Input time'),nl,
read(Y),nl,
( time(Y,X) ->
write(X),nl
; write('bad input'),nl ),
menu1.

How to merge this two if-else statement in prolog

can_go(Place) :- here(X), connect(X, Place).
can_go(Place) :- ('You cannnot get there from here'), nl,
fail.
can_go(location(treasure,room)) :- location(key,in_hand).
can_go(location(treasure,room)) :- write('cannot enter here without keys'), nl,
fail.
There are two distinct concepts involved here: Whether you can physically get from one place to another, and whether you are allowed to get from one place to another. In general, you should have different predicates for different concepts. Physical reachability is modeled in part by your connect/2 predicate, but let's make it more explicit:
reachable(Place) :-
here(X),
connect(X, Place).
Then, let's rename the other part of your definition to allowed_to_go/1:
allowed_to_go(location(treasure,room)) :-
location(key,in_hand).
allowed_to_go(location(treasure,room)) :-
write('cannot enter here without keys'), nl,
fail.
Now, you can go some place if that place is reachable, and you are allowed to go there. The conjunction ("and") is simply the comma (,):
can_go(Place) :-
reachable(Place),
allowed_to_go(Place).
can_go(Place) :-
write('You cannnot get there from here'), nl,
fail.
A problem that remains is that if a place is reachable but you are not allowed to go there, you will print two error messages, one of which is irrelevant. There are several ways to solve this. Some would probably suggest adding a cut (!), but you should avoid that if there are simpler, clearer choices. In this particular case, negation might be the simplest possibility. You will not get the spurious error if you add a guard to the second clause of can_go/1:
can_go(Place) :-
\+ reachable(Place),
write('You cannnot get there from here'), nl,
fail.
Another way would be to use the if-then-else operator (->). (Note: I didn't test any of this.)

Unbroken loops loops

Hi I have this problem that I can not solve.
I am a Prolog rookie and I've seen a ton of these family tree examples but none seem to address my problem.
Say I have
son(X, Y) :-
\+daughter(X, Y),
father(Y, X).
father(Y, X) :-
male(X),
son(X, Y).
and I call one of them, it will go back and forth between the conditions because each one would satisfy the one before, therefore resulting in a local stack error.
Most seem to recommend removing one of the definitions, but I need to answer father and son queries. Please, help, this seems so simple, but I just can not figure it out. How can I break after looping once?
TIA
You could:
1) you could use a wrapper predicate:
father(Y,X):-
male(X),
son_data(X,Y).
son(X,Y):-
son_data(X,Y).
son(X,Y):-
\+daughter(X, Y),
father(Y, X).
your database should look like
son_data(mike,steph).
....
father(nick,john).
....
(no son/2 entries)
2) use a prolog version that supports tabling (such as XSB) (or implement it; not such a good idea ofc)

Resources