Prolog, number of sections between points - prolog

I have a task to do in Prolog. It seems to be easy, but I am really novice in this kind of language and I can't get use to it.
I have to write a function path_L(a,z,N) , where a,z - edges o a "road", N is the variable that I am looking for. So, there are many edges defining one-way direction: edge(a,b), edge(b,c), edge(b,d), edge(e,f), etc. The goal of the path_L function is to give a result that is the number of sections between (a,z) in the form of N=result. So if for example:
path_L(a,b,N). -> N=1
path_L(a,c,N). -> N=2
I have already defined another function defining if a path between (X,Y) exists:
path(X,Y):-edge(X,Y).
path(X,Y):-edge(X,Z),path(Z,Y).

Assuming, like #lurker said, that you meant your predicates for finding a path are:
path(X,Y):- edge(X,Y).
path(X,Y):- edge(X,Z), path(Z,Y).
And given a few clauses stating the existance of edges as:
edge(a,b).
edge(b,c).
edge(c,d).
You got the right idea that path_L should have an additional argument for the 'count' of edges between nodes. A simple version of what you want (with some caveats) is below:
path_L(X,Y,1):- edge(X,Y).
path_L(X,Y,R):- edge(X,Z), path_L(Z,Y,N), R is N+1.
Notice how that the first case simply unifies the third argument with '1', so an objective clause like path_L(a,b,2) ("Distance between a and b is 2") correctly fails, while path_L(a,b,R) (notice R is a variable) succeeds with R unifying with 1. It's a neat feature of the paradigm that the same definition is good for 'both ways'.
Another example is the objective path_L(a,B,2) (notice B is a variable), which succeeds with B unifying with c - because c is the node with a distance of 2 from a.
Finally, assume instead a graph given by:
edge(a,b).
edge(b,c).
edge(c,d).
edge(d,x).
edge(a,x).
An objective clause like path_L(a,x,R) should first succeed with R = 1, and then, if requested (pressing ; on your terminal) with R = 4. This is because there are two valid paths (with lenghts 1 and 4) from a to x. The order of the predicates matters - if you defined path_L as such:
path_L(X,Y,R):- edge(X,A), path_L(A,Y,N), R is N+1.
path_L(X,Y,1):- edge(X,Y).
That same query would result first in R=4, and then in R=1. This is because the order in which predicates are defined matter. In fact, the mechanisms through which Prolog choose which predicates to test and how clauses are chosen for 'solving the problem' are well defined; and you should definitely look it up if you get into logic programming.
Hope this helps.
P.S.: about those caveats - e.g., none of the above allow a path of zero-length from a node to itself. Depending on what you want, that could be the case or a mistake.

Related

extracting algorithm for building prolog rules

I have to create some "list of predicates" in prolog.
But I don't fully understand the way of thinking
that I have to learn if I want to create some working predicates.
I've seen some popular tutorials (maybe I've not been searching precisely enough), but I can not find any tutorial that teaches how to plan an algorithm using REALLY elementary steps.
For example...
Task:
Write a concat(X,Y,Z). predicate that is taking elements from the lists X and Y and concatenates them in the list Z.
My analysing algorithm:
Firstly I define a domain of the number of elements I'll be concatenating (the lengths of the lists X and Y) as non-negative integers (XCount >= 0 and YCount >= 0). Then I create a predicate for the first case, which is XCount = 0 and YCount = 0:
concat([],[],[]).
... then test it and find that it is working for the first case.
Then, I create a predicate for the second case, where XCount = 1 and YCount = 0, as:
concat(X,[],X).
... and again test it and find that it is working with some unexpected positive result.
Results:
I can see that this algorithm is working not only for XCount = 1 but also for XCount = 0. So I can delete concat([],[],[]). and have only concat(X,[],X)., because X = [] inside predicate concat(X,[],X). is the same as concat([],[],[])..
The second unexpected result is that the algorithm is working not only for XCount in 0,1 but for all XCount >= 0.
Then I analyse the domain and search for elements that weren't handled yet, and find that the simplest way is to create a second predicate for YCount > 0.
Remembering that using just X as the first argument can cover all XCount >= 0, I create a case for YCount = 1 and all Xes, which is:
concat(X,[Y|_],[Y|X]).
And this is the place where my algorithm gets a brain-buffer overflow.
Respecting stackoverflow rules, I'm asking precisely.
Questions:
Is there any way to find the answer by myself? By which I mean - not an answer for the problem, but for the algorithm I've shown to solve it.
In the other words, the algorithm of my algorithm.
If you can answer question 1, how can I find this type of hint in future? Is there a specific name for my problem?
How precise do I have to be - how many cases and in what language can I try to implement my algorithm that is not just "doing" things, but is "thinking" how to plan and create other algorithms.
Lists are not defined as counts of elements in them. lists are defined recursively, as empty, or a pair of an element and the rest of elements:
list([]).
list([_A|B]) :- list(B).
Lists can be the same:
same_lists([], []).
same_lists([A|B], [A|C]) :- same_lists(B, C).
Or one can be shorter than the other, i.e. its prefix:
list_prefix([], L):- list(L).
list_prefix([A|B], [A|C]):- list_prefix(B, C).
Where the prefix ends, the suffix begins:
list_split([], L, L):- list(L).
list_split([A|B], Sfx, [A|C]):- list_split(B, Sfx, C).
So, general advice is: follow the types, how they are constructed, and analyze the situation according to all possible cases. With lists, it is either empty, or non-empty lists.

Prolog: redundant program points in failure-slice?

We are implementing diagnostic tools for explaining unexpected universal non-termination in pure, monotonic Prolog programs—based on the concept of the failure-slice.
As introduced in
the paper "Localizing and explaining reasons for nonterminating logic programs with failure slices", goals false/0 are added at a number of program points in an effort to reduce the program fragment sizes of explanation candidates (while still preserving non-termination).
So far, so good... So here comes my question1:
Why are there N+1 program points in a clause having N goals?
Or, more precisely:
How come that N points do not suffice? Do we ever need the (N+1)-th program point?
Couldn't we move that false to each use of the predicate of concern instead?
Also, we know that the program fragment is only used for queries like ?- G, false.
Footnote 1: We assume each fact foo(bar,baz). is regarded as a rule foo(bar,baz) :- true..
Why are there N+1 program points in a clause having N goals? How come that N points do not suffice?
In many examples, not all points are actually useful. The point after the head in a predicate with a single clause is such an example. But the program points are here to be used in any program.
Let's try out some examples.
N = 0
A fact is a clause with zero goals. Now even a fact may or may not contribute to non-termination. As in:
?- p.
p :-
q(1).
p.
q(1).
q(2).
We do need a program point for each fact of q/1, even if it has no goal at all, since the minimal failure slice is:
?- p, false.
p :-
q(1),
p, false.
q(1).
q(2) :- false.
N = 1
p :-
q,
p.
p :-
p.
q :-
s.
s.
s :-
s.
So here the question is: Do we need two program points in q/0? Well yes, there are different independent failure slices. Sometimes with false in the beginning, and sometimes at the end.
What is a bit confusing is that the first program point (that is the one in the query) is always true, and the last is always false. So one could remove them, but I think it is clearer to leave them, as a false at the end is what you have to enter into Prolog anyway. See the example in the Appendix. There, P0 = 1, P8 = 0 is hard coded.

Prolog Out of local stack error

I tried some basic example from a book and it makes "Out of local stack" error (I'll tell more after the code).
here is the code:
edge(a,b).
edge(a,e).
edge(b,d).
edge(b,c).
edge(c,a).
edge(e,b).
tedge(Node1,Node2) :-
edge(Node1,SomeNode),
edge(SomeNode,Node2).
edge(X,Y) :- tedge(X,Y).
I tried for example to write query edge(a,b) and it returned true and then I entered ';' and it made the error.
What's the problem here?
The problem is that you have defined what an edge is in a cyclic way. While looking for a second path from a to b Prolog is looping through tedge(a,VAR), edge(a,VAR), tedge(a,VAR), etc.
Notice that Prolog would loop even if you had not defined your edges to be symmetric, namely due to the cycle a -> b -> c -> a.
The solution is to keep track of vertices and/or edges that have been visited before. Checking for non-recurring vertices can be straightforwardly implemented using closure0/3 (a predicate I learned from SO user false). If you use that predicate, you only need your edge/2 facts and the following query:
?- closure0(edge, a, b).

Prolog - Towers of Hanoi

I'm trying to write a program to solve the towers of hanoi problem in Prolog. None of the posts here helped me so I decided to ask for myself. I have written the following code. It works well for 2 disks but goes into an infinite loop for 3.
hanoi(1,A,B,_,[(A,B)]).
hanoi(X,A,B,C,Y):-
hanoi(X2,A,C,B,Y1),
hanoi(1,A,B,_,[Y2]),
hanoi(X2,C,B,A,Y3),
append(Y1,[Y2|Y3],Y),
X2 is X-1.
It is called in the following way:
?- hanoi(3, a, b, c, Y).
a,b,c are the pegs. 3 is the number of disks and X is where we want the result.
I need to get the result in Y. I'm trying to recursively find the moves for X-1 discs from peg 1 to 3 using 2, 1 disc from peg 1 to 2, X-1 discs from peg 3 to 2 and append them. I can't understand what I'm doing wrong. Any help or guidance would be appreciated! Thanks!
The obvious problem -
When you have a conjunction, like:
a, b, c
You have two readings of this, logical and procedural. The logical reading would be:
"The conjunction is true if a is true, and b is true, and c is true."
The procedural reading is:
"The conjunction will succeed if a is evaluated and succeeds, then b is evaluated and it also succeeds, and then c is evaluated and it also succeeds." (or, to put it in other words, do a depth-first search of the solution space)
If you are careful enough, the procedural reading is not necessary to argue about your code. However, this is only the case when all subgoals in the conjunction have full overlap of the logical and the procedural reading.
The offender here is is/2. It does not have a purely logical meaning. It evaluates the right-hand operand (the arithmetic expression) and unifies the result with the left-hand (usually an unbound variable).
Here, you have a conjunction (in the body of the second clause) that says, in effect, "evaluate a(X), then, if it succeeds, find the value of X". The obvious problem is that a(X) requires the value of X to be evaluated in such a way that it terminates.
So, move the is before all subgoals that use its result, or look into using constraints.

Prolog - Merging Symbols (or Terms?)

(Pardon if my terminology is wrong... I'm new to Prolog.)
Suppose you have a series of symbols appearing in some unknown number of predicates.
f1(a, b, c, d).
f2(b, b, c).
...
fn(b, d, e).
Later--at runtime--you realize that terms a and b are the same, and you wish to merge them or replace one of them with the other. In other words, I would like to either:
Make a = b
Replace all instances of a with b
Replace a and b with a new symbol (made through gensym/2)
...or anything else that accomplishes this
... where I do not know which predicates use these terms.
Atoms that start with upper case letters are variables. The first step then is to use A and B. If at some point you decide two variables are actually equal, you just say it A = B. The process of stating logically that one thing = another is "unification".
e.g.
veryDifferentOrTheSame(A,B) :- veryDifferent(A,B).
veryDifferentOrTheSame(A,B) :- A = B.
Of course, unification won't always work. a(X) = b(X) will fail.
This all implies that when the code was written, you knew that you weren't sure A=B.
You can also dynamically assert clauses at runtime. Declaring a clause as dynamic and using assera or assertz.
But if you state:
iOwn(goldfish).
iOwnFish :- iOwn(fish).
and then want to make that work by saying "in my universe fish = goldfish", then you're in strange territory.

Resources