How to merge this two if-else statement in prolog - 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.)

Related

Prolog multiple unifications to one variable

I need to unify all terms in database to one variable.
For given code:
man('a').
man('b').
main :-
prompt(_, ''),
man(X),
write(X),
nl,
halt.
I get output:
a
I need to get something like:
['a','b']
Is it possible? I know about retract/1, which deletes the term from database, I could iterate trough all theese facts and retract them from database one by one, but that seems to be like shoting in the leg. Any given advice is apreciated.
If you have a collection of facts such as:
man(a).
man(b).
As was stated in the comments, you can find all the solutions with findall:
| ?- findall(X, man(X), Solutions).
Solutions = [a, b]
You can also modify your original program to use a failure-driven loop. fail in Prolog does just that: it fails, and so it causes Prolog to backtrack. Once man(X) fails to find more solutions, then the first clause of main will finally fail Prolog to the second clause which will simply succeed with no further action:
main :-
man(X),
write(X),
nl,
fail.
main.
Now if you query main you get:
| ?- main.
a
b
yes
| ?-
In the context of a broader program, findall/3 may be preferred since it captures the solutions for you, whereas the above merely "prints" them out without collecting them. Although, there are times when that's all that is desired.

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.

Custom subset not backtracking

I'm new to prolog and I've been having trouble with some homework.
On some part of my code I have to generate subsets of a given set on backtracking. Meaning, the code should try for a subset, and when it fails the next condition, try the next subset. I have done some research and the default function subset won't backtrack because as explained in this question both arguments are input arguments. So I built a custom one, which still isn't backtracking. Can you give me a hint on what I'm failing on? Here's my code:
numNutrients(8).
product(milk,[2,4,6]).
product(porkChops,[1,8]).
product(yoghurt,[3,1]).
product(honey,[5,7]).
product(plastic,[3,5,2]).
product(magic,[5,7,8]).
nutrientlist(N,L):-findall(I,between(1,N,I),L).
subset2([],[]):-!.
subset2([X|T],[X|T2]):-
subset2(T,T2).
subset2([_|T],[T2]):-
subset2(T,T2).
shopping(K,L):-
numNutrients(J),
nutrientlist(J,N),
findall(P,product(P,_),Z),
subset2(X,Z),
length(X,T),
T =< K,
covers(X,N),
L = X.
covers(_,[]):-!.
covers([X|L],N):-
product(X,M),
subset2(M,N),
subtract(N,M,T),
covers(L,T).
main:-
shopping(5,L),
write(L).
The problem is on predicate shopping(K,L). When it gets to predicate subset2, it gives the whole set, which has length 6 (not 5), then fails and doesn't backtrack. Since all previous predicates can't backtrack it just fails.
So, why doesn't subset2 backtrack?
Thank you for your time.
Primary focus: subset2/2
First, let us focus only on the predicate that shows different properties from those you expect.
In your case, this is only subset2/2, defined by you as:
subset2([], []) :- !.
subset2([X|T], [X|T2]) :-
subset2(T, T2).
subset2([_|T], [T2]) :-
subset2(T, T2).
I will now use declarative debugging to locate the cause of the problem.
For this method to apply, I remove the !/0, because declarative debugging works best on pure and monotonic logic programs. See logical-purity for more information. Thus, we shall work on:
subset2([], []).
subset2([X|T], [X|T2]) :-
subset2(T, T2).
subset2([_|T], [T2]) :-
subset2(T, T2).
Test cases
Let us first construct a test case that yields unintended answers. For example:
?- subset2([a], [a,b]).
false.
That obviously not intended. Can we generalize the test case? Yes:
?- subset2([a], [a,b|_]).
false.
So, we have now an infinite family of examples that yield wrong results.
Exercise: Are there also cases where the program is too general, i.e., test cases that succeed although they should fail?
Locating mistakes
Why have we seen unintended failure in the cases above? To locate these mistakes, let us generalize the program.
For example:
subset2(_, []).
subset2([_|T], [_|T2]) :-
subset2(T, T2).
subset2(_, [T2]) :-
subset2(T, T2).
Even with this massive generalization, we still have:
?- subset2([a], [a,b|_]).
false.
That is, we have many cases where we expect the query to succeed, but it fails. This means that the remaining program, even though it is a massive generalization of the original program, is still too specific.
Correcting the program
To make the shown cases succeed, we have to either:
add clauses that describe the cases we need
or change the existing clauses to cover these cases too.
For example, a way out would be to add the following clause to the database:
subset2([a], [a,b|_]).
We could even generalize it to:
subset2([a], [a|_]).
Adding either or both of these clauses to the program would make the query succeed:
?- subset2([a], [a,b|_]).
true.
However, that is of course not the general definition of subset2/2 we are looking for, since it would for example still fail in cases like:
?- subset2([x], [x,y|_]).
false.
Therefore, let us go with the other option, and correct the existing definition. In particular, let us consider the last clause of the generalized program:
subset2(_, [T2]) :-
subset2(T, T2).
Note that this only holds if the second argument is a list with exactly one element which is subject to further constraints. This seems way too specific!
Therefore, I recommend you start by changing this clause so that it at least makes the test cases collected so far all succeed. Then, add the necessary specializations to make it succeed precisely for the intended cases.

PROLOG: Trying to get around predsort deleting duplicate objects

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...

Prolog — symmetrical predicates

I have to simulate family tree in prolog.
And i have problem of symetrical predicates.
Facts:
parent(x,y).
male(x).
female(y).
age(x, number).
Rules:
blood_relation is giving me headache. this is what i have done:
blood_relation(X,Y) :- ancestor(X,Y).
blood_relation(X,Y) :- uncle(X,Y)
; brother(X,Y)
; sister(X,Y)
; (mother(Z,Y),sister(X,Z))
; (father(Z,Y),sister(X,Z))
; (father(Z,Y),brother(X,Z)).
blood_relation(X,Y) :- uncle(X,Z)
, blood_relation(Z,Y).
and I am getting i think satisfactory results(i have double prints - can i fix this), problem is that i want that this relation be symmetrical. It is not now.
blood_relation(johns_father, john):yes
blood_relation(john,johns_father): no
so..is there a way to fix this.
And i need query: All pairs that are not in blood_relation..
Update:
What kinds of relationships is the first statement supposed to satisfy?
blood_relation(X,Y):-blood_relation(X,Y).
sorry..it is a bad copy/paste..it
blood_relation(X,Y):-ancestor(X,Y).
Now fixed above.
here are other rules:
father(X,Y) :-
parent(X,Y),male(X).
mother(X,Y) :-
parent(X,Y),female(X).
brother(X,Y) :-
parent(Z,X),parent(Z,Y),
male(X).
sister(X,Y) :-
parent(Z,X),parent(Z,Y),
female(X).
grandFather(X,Y) :-
parent(Z,Y),parent(X,Z),
male(X).
grandMother(X,Y) :-
parent(Z,Y),
parent(X,Z),female(X).
uncle(X,Y) :-
mother(Z,Y),brother(X,Z).
ancestor(X,Y) :-
ancestor(X,Y).
ancestor(X,Y) :-
parent(X,Z),ancestor(Z,Y).
Mother's brother is in uncle definition. It's kind of strange. I've got rules that I need to implement, and I don't know how I can implement rules besides that. I'm just confused.
Any idea how to make blood_relation symmetric? And not_blood_relation is a new rule. And I need query. This one is really giving me headache. Maybe because relation is written like crap.
And there are no more facts. That's all. All rules, and all facts.
query.. not(blood_relation(X,Y)) doesn't work, and I really don't know why.
For example query:
age(X,Y), Y>18,
not(parent(X,Z)),write(X),nl,fail.
works just fine
The naive solution to making a particular predicate symmetric isn't that far from a decent one. For the sake of generality, let's look at a friendship relation so people don't get tripped up on uncles and the like.
Here are some facts detailing a friendship relation (where, say, the numbers are user ids and the particular ordering of the arguments came from who initiated the friendship).
friends(1,2).
friends(5,2).
friends(7,4).
You'd initially think a rule like "friends(A,B) :- friends(B,A)." would fix things right up, but this leads you to infinite recursion because it tells prolog that if it just swaps the argument one more time it might just work. There is a predicate called "#</2" that tells you whether one term (even a variable) comes before another in the "standard order of terms". The technical meaning isn't all that important here, but what we care about is that for two different terms it is only true for one ordering of them. We can use this to break the infinite recursion!
This single rule will take care of making "friend/2" symmetric.
friends(A,B) :- A #< B, friends(B,A).
As neat as this is, there is an approach way you should take for large projects. Recall that the ordering of the args in my list of facts had some actual meaning (who initiated the friendship). Adding the final rule destroyed future access to this information and, for other people reading the code, hides the symmetric property in a single line of code which is easy to ignore in the face of a block of hard-coded data.
Condsider the industrial-strength solution:
friended(1,2).
friended(5,2).
friended(7,4).
friends(A,B) :- friended(A,B).
friends(A,B) :- friended(B,A).
It is bulkier, but it reads cleanly without using obscure predicates and retains the original information (which you might want again someday in a real application).
--
As for finding pairs that don't have a specific property, make sure you always include some predicate to provide context in your rule when you use negation to look for actual individuals.
potential_enemies(A,B) :- user(A), user(B), \+ friends(A,B).
A bit looks like a homework, isn't it...
One trick which most of beginners of prolog don't think of is list pattern matching. Think of a tree like [a1,[[a2],[b2,[[e3],[f3]]],[c2]]] as in <tree>=[root,[<tree1>,<tree2>,...]]:
%Y is immediate child of X?
child(X,Y,[X|S]) :- member([Y|_],S).
%pick one tree in S and check
child(X,Y,[X|S]) :- member([Z|SS],S),child(Z,Y,[Z|SS]).
%X and Y end up with same root?
sib(X,Y,[R|T]) :- child(R,X,[R|T]), child(R,Y,[R|T]).
I think you can improve upon this like, using pairs as roots, adding genders, giving names to specific relations of members of the tree...
What kinds of relationships is the first statement supposed to satisfy?
blood_relation(X,Y):-blood_relation(X,Y).
That isn't telling you anything that you don't already "know" and is going to cause you recursion headaches. As for the 'no' answer, is looks like you've already gotten all of the answers from the query that you are going to get, and the interpreter is just telling you that there aren't any more.
You really should post more facts, and the definition of uncle/2, and is there a reason why you're not matching a mother's brother, just her sister? You have lots of other issues to work on :-).
For everything that is not a blood relation, try this:
not_blood_relation(X, Y) :- blood_relation(X, Y), !, fail.
not_blood_relation(X, Y).
And ask yourself why it works!

Resources