Getting all the solutions to a predicate in Prolog - prolog

I'm writing a text adventure game in Prolog, and I am printing out room exits. I have code that does:
exits_from(Room) :-
connected(Room, X),
write(X), write(' ').
where connected/2 is:
connected(X, Y) :- path(X, Y).
connected(X, Y) :- path(Y, X).
and path is:
path(room, hallway).
path(hallway, foyer).
and so on.
When I am printing the exits for a room though, it gets the first, then wants a ';' to say that I want another solution. Is there anyway to force a predicate to compute the result entirely, so that the player wouldn't have to keep asking for more exits?

one way is to do something like
print_all_solutions :-
solution(Sol),
write(Sol),
fail. % this causes backtracking
print_all_solutions. % succed
another is to use special predicate forall, like follows:
forall(solution(Sol), write(Sol))

Related

Prolog seemingly ending recursive call without going back up

I've got the following two edges defined:
edge(a,b).
edge(b,c).
add(X, L, [X | L]).
Now I'm trying to recursively build the path from a to c (a,b,c) using this:
path(FROM,TO,W):-
edge(FROM,TO),
add(TO, [], X),
add(FROM, X, W).
path(FROM,TO,W):-
edge(FROM,Y),
path(Y,TO, W),
add(FROM, W, _).
It seems to work fine in the base case as path(a,b,X) will output X = [a,b].
However, path(a,c,X) only outputs X = [b,c], as if it just gets to the base case and ends it there rather than going back up the recursive call.
Ultimately, I would like it to output X = [a,b,c] but I'm out of ideas.
FYI I'm using SWI-Prolog.
How can you identify the problem yourself? There is an easy way to do so. First, identify the cases you expect to be true and cases you expect it to fail. Here are some plus some auxiliary definitions to ease debugging
:- initialization(path(a,b,[a,b])).
:- initialization(path(a,c,[a,b,c])). % intended to be true, initially false
:- initialization(\+path(a,b,[a,_,c])).
:- initialization(\+path(a,d,_)). % no node d, thus no path
:- op(950, fy, *).
:- meta_predicate(*(0)).
*_G_0. % this permits us to remove goals by adding a * in front.
Now, save the program ..and say [program]. You will get a warning like
* user:user:path(a,c,[a,b,c]) - goal failed
So we know that we have not solved the problem.
Now, try to generalize the program by adding * in front of a goal. You will note that for each and every such generalization there will be more errors appearing (and in one case even non-termination). Except for the last goal where everything remains the same. So removing or replacing that goal seems to be a good idea.
Sooner or later you will encounter cycles. For acyclic paths, use path/4:
?- path(edge, Path, A, B).
Your code does not work correctly because it discards the result produced by the subgoal add(FROM, W, _), in the second clause of the predicate path/3. To solve the problem, you need to modify your code as following:
path(From, To, W):-
edge(From, To),
add(To, [], X),
add(From, X, W).
path(From, To, PATH):-
edge(From, Y),
path(Y,To, W),
add(From, W, PATH). % <== get PATH
An even better version of this code is as follows:
path(From, To, [From, To]) :-
edge(From, To).
path(From, To, [From|Path]) :-
edge(From, X),
path(X, To, Path).

Cutting the beginning of a clause and the relation between "cut", `!`, and `fail`

What does it mean to put the cut (!) at the very beginning of a clause?
p(X,Y) :- !, q(X), r(X,Y).
What is the difference between ! and fail and how are they related?
thanks.
I'm thinking that for fail, the predicate will just "fail" lol which is different from not backtracking? just want to be sure :)
Usually, you use this when you want to make sure that there is no backtracking on a certain combination of variable instantiations. To show some code (borrowed a bit from the SWI-Prolog implementation:
read_lines(In, Ls) :-
read_line_to_codes(In, Codes),
read_lines_rest(Codes, In, Ls).
read_lines_rest(end_of_file, _, []) :- !.
read_lines_rest(Codes, In, [Codes|Rest]) :-
read_line_to_codes(In, New_codes),
read_lines_rest(New_codes, In, Rest).
Now, with these predicates defined, you can read an input stream (a file, for example) to a list of lines. We are using read_line_to_codes/2 from library(readutil). It will unify its second argument with a list of codes representing a line, or the atom end_of_file at the end of input.
In the first clause of read_lines_read/3, we use unification in the head of the predicate definition. We "demand" that the first argument must be the atom end_of_file if we want the predicate to be even considered. When (at the end of input) this clause succeeds, the other possible solution in the second clause of the definition is not taken in consideration, and the predicate succeeds, closing up the list in the third argument.
Here it is used:
?- open('shortcut.pl', read, In), read_lines(In, Ls), forall(member(L,Ls), format("~s~n", [L])).
read_lines(In, Ls) :-
read_line_to_codes(In, Codes),
read_lines_rest(Codes, In, Ls).
read_lines_rest(end_of_file, _, []) :- !.
read_lines_rest(Codes, In, [Codes|Rest]) :-
read_line_to_codes(In, New_codes),
read_lines_rest(New_codes, In, Rest).
% variable instantiations
You should notice that the predicate succeeds exactly once. Try removing the cut in the first clause to see what happens.
As for the fail, yes, it makes the predicate fail (not succeed). At this point, if there are any choice points left, Prolog will backtrack to the most recent one.

Uncle or Aunt without sibling fact

I'm wondering how would I be able to call a nephew or niece in a family tree without using a sibling fact for prolog. I can't figure out a way to do it by calling the uncle directly.
For example:
parent(elli, lisa).
parent(kelly, lisa).
parent(ben, claire).
parent(lisa, claire).
grandparent(X, Y) :- parent(X, Z), parent(Z, Y).
Since ellie or kelly is ben's niece, I have the rule that when called by grandparent(X, claire). will give elli and kelly as the grandchild of claire. How would I make a rule to be able to call niece(ben, X). so it'll list all of ben's niece. I can't figure out a way to do it with only the parent fact, the only way of doing it would be to include a sibling fact but is there a way to do it without making a sibling fact?
I'm very new to prolog, literally like a week ago so please excuse me if I don't understand it that well.
brother(X, Y) :- male(X), parent(X, Z), parent(Y, Z), X \= Y.
uncle(X, Y) :- brother(X, Z), parent(Y, Z).

In prolog, why doesn't adding "edge(X, Y) :- edge(Y, X)." alone work for converting a directed graph definition to an undirected graph

I'm just learning Prolog, and I'm reviewing lecture notes and all the notes say is that:
given the following definition for directed graphs:
path(X, Y) :- edge(X, Y).
path(X, Y) :- edge(X, Z), path(Z, Y).
If we wanted to make this an undirected graph, defining
edge(X, Y) :- edge(Y, X). alone doesn't work, and I can't figure out why.
X to Y has an edge if Y to X has an edge. Seems to make sense to me.
The notes don't really clarify why not, but it does define that the proper solution would be:
edge1(X, Y) :- edge(X, Y).
edge1(X, Y) :- edge(Y, X).
to what we already had.
Can anyone explain this to me, please and thanks? <3
There are several potential sources of non-termination in your program.
First, with the rule edge(X, Y) :- edge(Y, X). your program will never terminate. Regardless of where you add this rule to your program.
What is often quite irritating is that your program will still produce many answers which somewhat suggests that it works. However, it will never stop.
The best way to understand this, is to consider a slightly modified program, called a failure-slice. This modified program will share many properties with your program. Not all of them, but some. We will add goals false into your program. If the resulting program loops, the original program will loop as well.
path(X, Y) :- edge(X, Y), false.
path(X, Y) :- false, edge(X, Z), path(Z, Y).
edge(a, b) :- false.
edge(X, Y) :- edge(Y, X).
Second, there is another source of non-termination in your improved program. Here is the related failure-slice:
path(X, Y) :- false, edge1(X, Y).
path(X, Y) :- edge1(X, Z), path(Z, Y), false.
edge1(X, Y) :- edge(X, Y).
edge1(X, Y) :- edge(Y, X).
edge(a, b).
edge1/2 now always contains cycles, so this fragment will loop for path(a, Y). and more generally also path(X, Y), false.
To solve this problem, you will have to rewrite path/2.
You can rewrite this in a generic manner by using closure0/3 and path/4!
So path/2 can be defined as:
path(X, Y) :-
closure(edge, X, Y).
Because rules don't work the same as facts, and you are spiraling off into an infinite loop if you use the same predicate.
Let's take the example ?-edge(5,2). We will end up calling --
edge(5,2) :- edge(2,5).
Ok, what happens when we call edge(2,5)?
edge(2,5) :- edge(5,2).
... Uh oh. Logic circle.
When using edge1, you are simply creating a wrapper for your predicate to escape the recursive definition.

run function with all possibilities resulted from other function

I have two predicates:
foo(Y,X)
bar(Y,Z)
After running foo, How can I run bar with all possibilities of Y ?
example:
foo(Y, key) % all possibilities of Y => chat
% faq
% about
% search
How can I run bar with these all possibilities ?
bar(chat, Z)
bar(faq, Z)
bar(about, Z)
bar(serach, Z)
And then store all the results of Z in a list Zs?
foo/2 and bar/2 are already in join, and after each run of foo/2 bar/2 will be tried.
Maybe you are looking for forall(foo(Y,X), bar(Y,Z)), that run all possibilities of foo/2, and then bar/2. I.e. is required that bar/2 doesn't fail.
To understand the behaviour of forall/2, as well as other all solutions builtins, like setof/3, can be useful test with very simple builtins, with well known behaviour:
?- forall(member(X,[f,o,o]),(member(Y,[b,a,r]),writeln(X-Y))).
f-b
o-b
o-b
true.
You can see that the complete solution search of forall applies to its first argument, not the second.
HTH
allZs(X, Zs) :-
setof(Y, foo(Y, X), Ys),
maplist(bar, Ys, Zs).
related SWI-Prolog man pages: Finding all Solutions to a Goal and library apply
Note: usually in Prolog the convention is to put intput arguments before output ones - in your first predicate that'd mean foo(X, Y) instead of foo(Y, X). Plus here it'd outline the transitivity: foo(X, Y), bar(Y, Z)..
I think you want something like this:
barOnList([], []).
barOnList([Y|Ys], [Z|Zs]) :- bar(Y, Z), barOnList(Ys, Zs).

Resources