GNU Prolog - Recursion issue (easy?) - recursion

Ok, so i have this
edu_less(hs,college).
edu_less(college,masters).
edu_less(masters,phd).
I need to write a function to tell if something is less than the other. The predicate is
edu_le.
So if i put edu_le(hs,phd). it should return yes.
I came up with this.
edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,B).
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).
I really don't want it to go through everything and return multiple answers.
Is it possible to only return yes or no if it finds that it is in fact less than or equal to the 2nd argument?
So basically if i use the example edu_le(hs,phd) again, then because hs is less than college, and college is than masters, and masters is less than phd, then hs must be less than phd and it would say yes.
Sorry, really new to prolog, still trying to get the hang of this.

In the predicate definition
edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,B).
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).
the second clause is superfluous and causes repeated generation of answers. Use
edu_le(A,B) :- A = B.
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).
This gives you one true answer, then no more answers (false) on backtracking. You can use a cut in the first clause, but then generating won't work anymore.
?- edu_le(hs,X).
X = hs ;
X = college ;
X = masters ;
X = phd ;
false.
becomes incomplete:
?- edu_le(hs,X).
X = hs.
As mat suggested, use once/1 instead. In a good Prolog implementation, this predicate works as if your program had cuts in strategic places, speeding up your program without disturbing its logical semantics.

The most practical way to write predicates like that is to use the cut (!). The cut causes further clauses not to be considered when backtracking. You would write your predicate as following:
edu_le(A,B) :- A = B, !.
edu_le(A,B) :- edu_less(A,B), !.
edu_le(A,B) :- edu_less(A,C), edu_le(C,B).
The last clause does not need a cut because there are no further clauses to consider anyway. The cut is placed after any tests to determine whether the clause should succeed.
Logic programming purists disapprove of the cut, because it makes the meaning of a predicate depend on the ordering of the clauses, which is unlike logic in mathematics.

!/0 also makes this program incomplete, consider for example the most general query with both versions:
?- edu_le(X, Y).
It is often better to use once/1 if you only want a single proof of a particular goal:
?- once(edu_le(hs, phd)).

I would suggest you NOT to follow the path proposed by Juho Östman and keep purity - otherwise, why should you use Prolog in first instance? If you are too lenient with sticking to the logical paradigm you obtain some unpleasing results. In this case, Juho's predicate is definitely different from yours, and I'll show you why.
First, just drop the useless edu_le(A,B) :- edu_less(A,B). rule, as larsmans suggests. You will obtain a less redundant version of your original predicate:
edu_le1(A, A).
edu_le1(A, B) :- edu_less(A, C), edu_le1(C, B).
It just behaves as edu_le, meaning: given an arbitrary query, it produces exactly the same answer, except for duplicates (edu_le1 has less). You may just be happy with it, but it still has some redundant answers that you may not like; e.g, under SWI:
?- edu_le1(hs, hs)
true ;
false.
Now you may say you do not like it because it still has the redundant false, but if you use Juho's predicate instead (without the useless rule):
edu_le2(A, A) :- !.
edu_le2(A, B) :- edu_less(A, C), edu_le2(C, B).
it's true that you eliminate the useless final false:
?- edu_le2(hs, hs)
true.
?-
but you lose more than that: You lose, as mat remarks, the possibility of generating all the solutions when one variable is not instantiated:
?- edu_le1(hs, B) %same, with more copies, for edu_le
B = hs ;
B = college ;
B = masters ;
B = phd ;
false.
?- edu_le2(hs, B)
B = hs. %bad!
?-
In other words, the latter predicate is NOT equivalent to the former: edu_le and edu_le1 have type edu_le(?A, ?B), while instead edu_le2 has type edu_le2(+A, +B) (see [1] for the meaning). Be sure: edu_le2 is less useful because it does less things, and thus can be reused in less contexts. This because the cut in edu_le2 is a red cut, i.e., a cut that changes the meaning of the predicate where it is introduced. You may nevertheless be content with it, given that you understand the difference between the two. It all depends on what you want to do with it.
If you want to get the best of the two worlds, you need to introduce in edu_le1 a proper green cut that lowers the redundancy when A and B are completely instantiated to terms. At the purpose, you must check that A and B are instantiated to the same term before cutting. You cannot do it with =, because = does not check, but unifies. The right operator is ==:
edu_le3(A, B) :- (A == B -> ! ; true), A = B.
edu_le3(A, B) :- edu_less(A, C), edu_le3(C, B).
Note that the additional cut in the first rule is active only when A and B happen to be the same term. Now that the cut is a proper green cut, the predicate works also in the most general cases as your original one:
?- edu_le3(A, A).
true.
?- edu_le3(A, B). %note that A and B are not the same term
A = B ;
A = hs,
B = college ;
A = hs,
B = masters ;
A = hs,
B = phd ;
A = college,
B = masters ;
A = college,
B = phd ;
A = masters,
B = phd ;
false.
?-
with Prolog backtracking through all the solutions.
I don't think there is some way to eliminate the last false without introducing too strong dependency on edu_lt. This because we must keep open the possibility that there is another edu_lt to explore, in the case you decide later to enrich it with more ground facts. So, in my opinion, this is the best you can have.
[1] SWI Prolog reference manual, section 4.1.

Related

Recursive search & Accumulators & Counters in Prolog

After a long search on google I couldn't find a clear answer of this:
In Prolog doing recursion by itself its easy. My main problem is understanding where to place accumulators and counters. Here is an example:
nXlist(N,X,[X|T]):-
N \=0,
N1 is N-1,
nXList(N1,X,T).
nXList(0,_,[]).
media([X|L], N, Soma):-
media(L, N1, Soma1),
N is N1 + 1,
Soma is Soma1 + X.
media([], 0, 0).
On the first example i used a counter BEFORE the recursion but in the second example I use it AFTER. The reason I have done that is the called try and see cause i really can't understand why sometimes is before and sometimes is after...
Maybe the central point of your question is in the preamble:
In Prolog doing recursion by itself its easy
It's not easy, it's mandatory. We don't have loops, because we don't have a way to control them. Variables are assign once.
So, I think the practical answer is rather simple: if the 'predicate' (like is/2) needs a variable value, you ground the variable before calling it.
To me, it helps to consider a Prolog program (a set of clauses) as grammar productions, and clause arguments as attributes, either inherited (values computed before the 'instruction pointer') or synthesized (values computed 'here', to be returned).
update: Most importantly, if the recursive call is not last, the predicate is not tail recursive. So, having anything after the recursive call should be avoided if possible. Notice that both definitions in the answer by user false are tail recursive, and that's precisely due to the fact that the arithmetic conditions there are placed before the recursive call, in both of them. That's so basic, that we have to make an effort to notice it explicitly.
Sometimes we count down, sometimes we count up. I discuss this in another answer at length. It talks of accumulators, befores and afters. :)
There's also this thing called "associativity" of an operation (say, +), where
a+(b+(c+....)) == (a+b)+(c+...)
that lets us regroup and (partially) calculate sooner rather than later. As soon as possible, but not sooner.
Short answer: you can place such arithmetical relations both before and thereafter. At least, if you are using constraints in place of (is)/2. The only difference may be in termination and errors.
So let's see how your predicates can be defined with constraints:
:- use_module(library(clpfd)).
nXList(0,_,[]).
nXList(N,X,[X|T]):-
N #> 0,
N1 #= N-1,
nXList(N1,X,T).
media([], 0, 0).
media([X|L], N, Soma):-
N #> 0,
N #= N1 + 1,
Soma #= Soma1 + X,
media(L, N1, Soma1).
You can now use these definitions in a much more general way, say:
?- nXList(3, X, T).
T = [X, X, X]
; false.
?- media(Xs, 3, S).
Xs = [_A, _B, _C], _D+_A#=S, _C+_B#=_D
; false.
... nXList/3 can be more compactly expressed by:
..., length(T, N), maplist(=(X), T), ...

Avoiding infinite recursion but still using unbound parameter passing only

I have the following working program: (It can be tested on this site: http://swish.swi-prolog.org, I've removed the direct link to a saved program, because I noticed that anybody can edit it.)
It searches for a path between two points in an undirected graph. The important part is that the result is returned in the scope of the "main" predicate. (In the Track variable)
edge(a, b).
edge(b, c).
edge(d, b).
edge(d, e).
edge(v, w).
connected(Y, X) :-
(
edge(X, Y);
edge(Y, X)
).
path(X, X, _, []) :-
connected(X, _).
path(X, Y, _, [X, Y]) :-
connected(Y, X).
path(X, Z, Visited, [X|Track]) :-
connected(X, Y),
not(member(X, Visited)),
path(Y, Z, [X|Visited], Track).
main(X, Y) :-
path(X, Y, [], Track),
print(Track),
!.
Results:
?- main(a, e).
[a, b, d, e]
true
?- main(c, c).
[]
true
?- main(b, w).
false
My questions:
The list of visited nodes is passed down to the predicates in 2 different ways. In the bound Visited variable and in the unbound Track variable. What are the names of these 2 different forms of parameter passing?
Normally I only wanted to use the unbound parameter passing (Track variable), to have the results in the scope of the main predicate. But I had to add the Visited variable too, because the member checking didn't work on the Track variable (I don't know why). Is it possible to make it work with only passing the Track in an unbound way? (without the Visited variable)
Many thanks!
The short answer: no, you cannot avoid the extra argument without making everything much messier. This is because this particular algorithm for finding a path needs to keep a state; basically, your extra argument is your state.
There might be other ways to keep a state, like using a global, mutable variable, or dynamically changing the Prolog data base, but both are more difficult to get right and will involve more code.
This extra argument is often called an accumulator, because it accumulates something as you go down the proof tree. The simplest example would be traversing a list:
foo([]).
foo([X|Xs]) :-
foo(Xs).
This is fine, unless you need to know what elements you have already seen before getting here:
bar(List) :-
bar_(List, []).
bar_([], _).
bar_([X|Xs], Acc) :-
/* Acc is a list of all elements so far */
bar_(Xs, [X|Acc]).
This is about the same as what you are doing in your code. And if you look at this in particular:
path(X, Z, Visited, /* here */[X|Track]) :-
connected(X, Y),
not(member(X, Visited)),
path(Y, Z, [X|Visited], /* and here */Track).
The last argument of path/4 has one element more at a depth of one less in the proof tree! And, of course, the third argument is one longer (it grows as you go down the proof tree).
For example, you can reverse a list by adding another argument to the silly bar predicate above:
list_reverse(L, R) :-
list_reverse_(L, [], R).
list_reverse_([], R, R).
list_reverse_([X|Xs], R0, R) :-
list_reverse_(Xs, [X|R0], R).
I am not aware of any special name for the last argument, the one that is free at the beginning and holds the solution at the end. In some cases it could be an output argument, because it is meant to capture the output, after transforming the input somehow. There are many cases where it is better to avoid thinking about arguments as strictly input or output arguments. For example, length/2:
?- length([a,b], N).
N = 2.
?- length(L, 3).
L = [_2092, _2098, _2104].
?- length(L, N).
L = [],
N = 0 ;
L = [_2122],
N = 1 ;
L = [_2122, _2128],
N = 2 . % and so on
Note: there are quite a few minor issues with your code that are not critical, and giving that much advice is not a good idea on Stackoverflow. If you want you could submit this as a question on Code Review.
Edit: you should definitely study this question.
I also provided a somewhat simpler solution here. Note the use of term_expansion/2 for making directed edges from undirected edges at compile time. More important: you don't need the main, just call the predicate you want from the top level. When you drop the cut, you will get all possible solutions when one or both of your From and To arguments are free variables.

How to create a Prolog predicate that removes 2nd to last element?

I need help creating a predicate that removes the 2nd to last element of a list and returns that list written in Prolog. So far I have
remove([],[]).
remove([X],[X]).
remove([X,Y],[Y]).
That is as far as I've gotten. I need to figure out a way to recursively go through the list until it is only two elements long and then reassemble the list to be returned. Help with explanation if you can.
Your definition so far is perfect! It is a little bit too specialized, so we will have to extend it. But your program is a solid foundation.
You "only" need to extend it.
remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X,_,Y], [X,Y]).
remove([X,Y,_,Z], [X,Y,Z]).
remove([X,Y,Z,_,Z2], [X,Y,Z,Z2]).
...
OK, you see how to continue. Now, let us identify common cases:
...
remove([X,Y,_,Z], [X,Y,Z]).
% ^^^ ^^^
remove([X,Y,Z,_,Z2], [X,Y,Z,Z2]).
% ^^^^^ ^^^^^
...
So, we have a common list prefix. We could say:
Whenever we have a list and its removed list, we can conclude that by adding one element on both sides, we get a longer list of that kind.
remove([X|Xs], [X|Ys]) :-
remove(Xs,Ys).
Please note that the :- is really an arrow. It means: Provided what is true on the right-hand side, also what is found on the left-hand side will be true.
H-h-hold a minute! Is this really the case? How to test this? (If you test just for positive cases, you will always get a "yes".) We don't have the time to conjure up some test cases, do we? So let us let Prolog do the hard work for us! So, Prolog, fill in the blanks!
remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X|Xs], [X|Ys]) :-
remove(Xs,Ys).
?- remove(Xs,Ys). % most general goal
Xs = [], Ys = []
; Xs = [A], Ys = [A]
; Xs = [_,A], Ys = [A]
; Xs = [A], Ys = [A] % redundant, but OK
; Xs = [A,B], Ys = [A,B], unexpected % WRONG
; Xs = [A,_,B], Ys = [A,B]
; Xs = [A,B], Ys = [A,B], unexpected % WRONG again!
; Xs = [A,B,C], Ys = [A,B,C], unexpected % WRONG
; Xs = [A,B,_,C], Ys = [A,B,C]
; ... .
It is tempting to reject everything and start again from scratch.
But in Prolog you can do better than that, so let's calm down to estimate the actual damage:
Some answers are incorrect. And some answers are correct.
It could be that our current definition is just a little bit too general.
To better understand the situation, I will look at the unexpected success remove([1,2],[1,2]) in detail. Who is the culprit for it?
Even the following program slice/fragment succeeds.
remove([],[]).
remove([X],[X]) :- false.
remove([_,X],[X]) :- false.
remove([X|Xs], [X|Ys]) :-
remove(Xs,Ys).
While this is a specialization of our program it reads: that remove/2 holds for all lists that are the same. That can't be true! To fix the problem we have to do something in the remaining visible part. And we have to specialize it. What is problematic here is that the recursive rule also holds for:
remove([1,2], [1,2]) :-
remove([2], [2]).
remove([2], [2]) :-
remove([], []).
That kind of conclusion must be avoided. We need to restrict the rule to those cases were the list has at least two further elements by adding another goal (=)/2.
remove([X|Xs], [Y|Ys]) :-
Xs = [_,_|_],
remove(Xs, Ys).
So what was our error? In the informal
Whenever we have a list and its removed list, ...
the term "removed list" was ambiguous. It could mean that we are referring here to the relation remove/2 (which is incorrect, because remove([],[]) holds, but still nothing is removed), or we are referring here to a list with an element removed. Such errors inevitably happen in programming since you want to keep your intuitions afresh by using a less formal language than Prolog itself.
For reference, here again (and for comparison with other definitions) is the final definition:
remove([],[]).
remove([X],[X]).
remove([_,X],[X]).
remove([X|Xs], [X|Ys]) :-
Xs = [_,_|_],
remove(Xs,Ys).
There are more efficient ways to do this, but this is the most straight-forward way.
I will try to provide another solution which is easier to construct if you only consider the meaning of "second last element", and describe each possible case explicitly:
rem_2nd_last([], []).
rem_2nd_last([First|Rest], R) :-
rem_2nd_last_2(Rest, First, R). % "Lag" the list once
rem_2nd_last_2([], First, [First]).
rem_2nd_last_2([Second|Rest], First, R) :-
rem_2nd_last_3(Rest, Second, First, R). % "Lag" the list twice
rem_2nd_last_3([], Last, _SecondLast, [Last]). % End of list: drop second last
rem_2nd_last_3([This|Rest], Prev, PrevPrev, [PrevPrev|R]) :-
rem_2nd_last_3(Rest, This, Prev, R). % Rest of list
The explanation is hiding in plain view in the definition of the three predicates.
"Lagging" is a way to reach back from the end of the list but keep the predicate always deterministic. You just grab one element and pass the rest of the list as the first argument of a helper predicate. One way, for example, to define last/2, is:
last([H|T], Last) :-
last_1(T, H, Last).
last_1([], Last, Last).
last_1([H|T], _, Last) :-
last_1(T, H, Last).

Checking implicit rule predicate in Prolog

I hope I've used an understandable title. What I want to achieve is prolog to determine a relation even though it isn't explicitly stated.
brother(anders, jason).
sister(anders, madonna).
siblings(X,Y) :- sister(X,Y).
siblings(X,Y) :- brother(X,Y).
siblings(X,Y) :- siblings(X,Z), siblings(Z,Y).
It's the sibling rule I'm struggling with. Clearly Jason is Anders brother, and Madonna is Anders sister. Jason and Madonna is therefor Anders' siblings. They are also siblings with each other even though this isn't explicitly stated. How do I make prolog determine this by checking the siblings of a sibling? It's my last rule that will cause a stack overflow \o/ which really isn't that much of a surprise. Is this achievable in a recursive fashion or do I need to write more rules like:
siblings(X,Y) :- sister(X,Z), brother(Z,Y).
siblings(X,Y) :- sister(X,Z), sister(Z,Y).
siblings(X,Y) :- brother(X,Z), sister(Z,Y).
siblings(X,Y) :- brother(X,Z), brother(Z,Y).
The above would not work if we had a very large family and even bigger gaps in our brother/sister facts, that could mean that I have to check if a person is sibling to my siblings sibling and so on.
It might not be a good real world example but I'm looking for the concept of handling these situation rather than a solution to a specific problem.
It might work automatically in a Prolog with tabling (YAP or XB) given the appropriate annotation, but most of us live within the limitations of standard-ish Prolog. Your additional rules are really just pushing food around on your plate. You can see you would need ever more of them to handle a situation like this:
brother(anders, brian).
brother(celeste, donal).
sister(brian, celeste).
If you want sibling(anders, X) to ever unify X with donal you're going to need a bigger boat.
Let's call this your recursive rule:
siblings(X,Y) :- siblings(X,Z), siblings(Z,Y).
This can't ever succeed, because in order to succeed, it must first succeed. The stack overflow isn't happening because you're calling siblings/2 recursively, it's happening because it calls it twice to fulfill it once. It spends two dollars to earn one. But you can break even by spending what you make, by replacing that rule with this:
siblings(X, Z) :- brother(X, Y), siblings(Y, Z).
siblings(X, Z) :- sister(X, Y), siblings(Y, Z).
This is a capital improvement, but it isn't enough. This basically gets us directed siblinghood. Brian is Anders's brother, but Anders isn't Brian's brother. The problem is severe enough that we'll never acknowledge Anders and Donal's siblinghood. We can fix that by adding a few more rules:
siblings(X, Y) :- brother(X, Y) ; brother(Y, Z).
siblings(X, Y) :- sister(X, Y) ; sister(Y, X).
These rules make brotherhood and sisterhood undirected. Now you get all the results you'd ever want:
?- siblings(anders,Y).
Y = brian ;
Y = anders ;
Y = celeste ;
Y = donal ;
Y = brian ;
Y = celeste ;
false.
Bet you can figure out how to eliminate the self-siblinghood problem. The other duplicates may turn out to be more troublesome. Give it a shot and let us know if you get stuck.
that's transitive closure you want:
sibl(A,B):- brother(A,B) ; brother(B,A) ; sister(A,B) ; sister(B,A).
sibl_trans(A,B):- sibl(A,B).
sibl_trans(A,B):- sibl(A,C), sibl_trans(C,B).
Your definition is like
sibl_trans(A,B):- sibl(A,B).
sibl_trans(A,B):- sibl_trans(A,C), sibl_trans(C,B).
This is left recursion. Not good, with depth-first search of Prolog. Could work with iterative deepening.
This question comes up very frequently.

Prolog Recursion - Satisfying Both Directions (Simple)

I am very new to Prolog and I was given this assignment.
My code is as follows:
relatives(cindy,tanya).
relatives(tanya,alan).
relatives(alan,mike).
relatives(kerry,jay).
relatives(jay,alan).
isRelated(X,Y):-
relatives(X,Y).
isRelated(X,Y):-
relatives(X,Z),
isRelated(Z,Y).
Simple enough. This shows that if:
?- isRelated(cindy,mike).
Prolog will return true. Now, I'm stuck on how to make it return true if:
?- isRelated(mike,cindy).
I've been trying to come up with ideas like if isRelated(Z,Y) returns false, then switch X and Y, and run isRelated again. But I'm not sure if Prolog even allows such an idea. Any hints or advice would be greatly appreciated. Thanks!
UPDATE:************************************
So I added:
isRelated(X,Y):-
relatives(X,Y);
relatives(Y,X).
That will satisfy "direct" relationships, but simply enough I found out that it doesn't satisfy indirect relationships.
I really want to do something like, if the initial query:
isRelated(mike,cindy)
fails, then try and see if the reverse is true by switching X and Y:
isRelated(cindy,mike)
That will definitely return true. I just don't know how to do this syntactically in Prolog.
Further hint to those in the comments, as I can't leave comments yet: With your original set of rules and facts,
isRelated(cindy,tanya) is true, but isRelated(tanya,cindy) is not, so you need to make isRelated(X,Y) symmetric; what simple addition to isRelated would achieve that?
Also, you could try drawing a graph of the relation relatives(X,Y), with an arrow from X to Y for all your base facts, and see if that helps you think about how the Prolog interpreter is going to attempt to satisfy a query.
So to answer your last question, you don't switch the values of X and Y in Prolog, like you would call swap(x,y) in C, say. The value held by a logic variable can not be changed explicitly, only back-tracked over. But you can easily use Y where you would use X, and vice versa:
somePred(X,Y):- is_it(X,Y).
somePred(X,Y):- is_it(Y,X).
This defines somePred predicate as a logical disjunction, an "OR". It can be written explicitly too, like
somePred(X,Y):- is_it(X,Y) ; is_it(Y,X).
Note the semicolon there. A comma , between predicates OTOH defines a conjunction, an "AND" (a comma inside a compound term just serves to delimit the term's "arguments").
YOu're almost there, you're just trying, I think, to cram too much stuff into one predicate.
Write the problem statement in English and work from that:
A relationship exists between two people, X and Y
if X and Y are directly related, or
if any direct relative of X, P, is related to Y.
Then it gets easy. I'd approach it like this:
First, you have your set of facts about relatives.
related( cindy, tanya ).
...
related( james, alan ).
Then, a predicate describing a direct relationship is terms of those facts:
directly_related( X , Y ) :- % a direct relationship exists
related(X,Y) % if X is related to Y
. % ... OR ...
directly_related( X , Y ) :- % a direct relationship exists
related(Y,X) % if Y is related to X
. %
Finally, a predicate describing any relationship:
is_related(X,Y) :- % a relationship exists between X and Y
directly_related(X,Y) % if a direct relationship exists between them
. % ... OR ...
is_related(X,Y) :- % a relationship exists between X and Y
directly_related(X,P) , % if a direct relationship exists between X and some other person P
is_related(P,Y) % and [recursively] a relationship exists between P and Y.
. %
The solution is actually more complicated than this:
The facts about relationships describe one or more graphs. More on graphs at http://web.cecs.pdx.edu/~sheard/course/Cs163/Doc/Graphs.html. What you're doing is finding a path from node X to Node Y in the graph.
If the graphs described by the facts about relationships have one or more paths between X and Y, the above solution can (and will) succeed multiple times (on backtracking), once for every such path. The solution needs to be deterministic. Normallly, having established that two people are related, we're done: just because I have two cousins doesn't mean I'm related to my aunt twice.
If the graph of relationships contains cycles (almost certainly true) such that a "circular" path exists: A → B → C → A …, the solution is susceptible to unlimited recursion. That means the solution needs to detect and deal with cycles. How might that be accomplished?

Resources