Related
Let's say I want to implement a predicate that 'returns' a list of all elements shared by a list of lists.
I can implement it as a one clause (looks bit ugly for logic programing):
shared_members(Members, Lists) :-
Lists = [] ->
Members = []
; findall(M, (maplist(member(M), Lists)), Members).
or as a set of clauses:
shared_members([], []). % possibly adding cut here to increase effciency
shared_members(Members, Lists) :-
findall(M, (maplist(member(M), Lists)), Members).
Which implementation is considered to be more efficient?
I know it depends on the Prolog implementation but maybe there is a general stand about efficiency of these cases.
In this case, you don't even need the first clause shared_member([], []). The findall/3 call will result in Members = [] if Lists = [].
The question is still interesting, though, and so we'll ignore this for now. You could run some stats to determine time-efficiency. Memory efficiency difference is negligible. The second approach given, however, is considered to be the canonical approach in Prolog. But they are also not equivalent in their behavior. The "if-else" in Prolog, as represented by p1 -> p2 ; p3 cuts removes the choice point after evaluation of p1. It's equivalent to p1, !, p2 ; p3.
Here's why this matters. I'll use a contrived example (which also does not require both clauses, but illustrates the point). I'll define a len/2 predicate that is true if the first argument is the length of the second:
len(0, []).
len(N, L) :- length(L, N).
Obviously, as in the case of the original problem, the first clause here is redundant, but it is important for this illustration. If I run this query, I get the following results:
| ?- len(N, [a,b,c]).
N = 3
yes
| ?- len(3, L).
L = [_,_,_]
yes
| ?- len(N, L).
L = []
N = 0 ? ;
L = []
N = 0 ? ;
L = [_]
N = 1 ? ;
L = [_,_]
N = 2 ? ;
L = [_,_,_]
N = 3 ?
Note that if both arguments are variable, it enumerates solutions. (Also, due to the redundant first clause, one of the solutions appears twice.)
Let's rewrite this predicate using "if-else":
len(N, L) :-
( L = []
-> N = 0
; length(L, N)
).
And we'll run it:
| ?- len(N, [a,b,c]).
N = 3
yes
So far, so good. But...
| ?- len(3, L).
no
| ?- len(N, L).
L = []
N = 0
yes
| ?-
Yikes! This is quite different. What happened?
In the second approach, ( L = [] -> N = 0 ; length(L, N) ) first attempts to unify L and []. If L is a variable, this succeeds with L = []. Since it succeeded, Prolog then attempts to unify N = 0. But with the query len(3, L), N is already bound to 3. So N = 0 fails and the entire clause fails.
Using the -> ; construct then, in this case, greatly reduces the generality of the implementation and yields incorrect results in some of the call scenarios.
I'm trying to rewrite code from Haskell to Prolog.
count :: Eq a => a -> [a] -> Int
count x = length . filter (x==)
f :: [Integer] -> [Integer]
f [] = []
f list = filter (\x -> count x list == 1) list
This code return list that contains elements that appears only once in the list.
So if I have list [1,1,2,2,3,4,4,5] this function returns [3,5]
I tried to find filter construction in Prolog but seems there no such thing. How can I make similar function in Prolog ?
To the existing answers, I would like to add an answer that is quite general in the sense that you can use it in multiple directions.
Building block: list_element_number/3
I start with the following predicate, defining a relation between:
a list Ls0
an element E
the number N of occurrences of E in Ls0
Here it is:
list_element_number(Ls0, E, N) :-
tfilter(=(E), Ls0, Ls),
length(Ls, N).
This solution uses tfilter/3 from library(reif). The predicate subsumes the function count you have posted. The main benefit of this predicate over the function is that the predicate can be used not only in those cases that even Haskell can do easily, such as:
?- list_element_number([a,b,c], a, N).
N = 1.
No, we can use it also in other directions, such as:
?- list_element_number([a,b,c], X, 1).
X = a ;
X = b ;
X = c ;
false.
Or even:
?- list_element_number([a,b,E], X, 2).
E = X, X = a ;
E = X, X = b ;
false.
Or even:
?- list_element_number([A,B,C], X, 3).
A = B, B = C, C = X ;
false.
And even in the most general case, in which all arguments are fresh variables:
?- list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [E, E],
N = 2 ;
Ls = [E, E, E],
N = 3 .
We can fairly enumerate all answers like this:
?- length(Ls, _), list_element_number(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [_160],
N = 0,
dif(E, _160) ;
Ls = [E, E],
N = 2 .
Main predicate: list_singletons/2
Using this building block, we can define list_singletons/2 as follows:
list_singletons(Ls, Singles) :-
tfilter(count_one(Ls), Ls, Singles).
count_one(Ls, E, T) :-
list_element_number(Ls, E, Num),
cond_t(Num=1, true, T).
This uses cond_t/3 and (again) tfilter/3 from library(reif).
Sample queries
Here are a few sample queries. First, the test case you have posted:
?- list_singletons([1,1,2,2,3,4,4,5], Singles).
Singles = [3, 5].
It works as desired.
Now a case involving variables:
?- list_singletons([A,B], Singles).
A = B,
Singles = [] ;
Singles = [A, B],
dif(A, B).
On backtracking, all possibilities are generated: Either A = B holds, and in that case, there is no element that occurs only once. Or A is different from B, and in that case both A and B occur exactly once.
As a special case of the above query, we can post:
?- list_singletons([A,A], Singles).
Singles = [].
And as a generalization, we can post:
?- length(Ls, _), list_singletons(Ls, Singles).
Ls = Singles, Singles = [] ;
Ls = Singles, Singles = [_7216] ;
Ls = [_7216, _7216],
Singles = [] ;
Ls = Singles, Singles = [_7828, _7834],
dif(_7828, _7834) ;
Ls = [_7216, _7216, _7216],
Singles = [] ;
Ls = [_7910, _7910, _7922],
Singles = [_7922],
dif(_7910, _7922) .
Enjoy the generality of this relation, obtained via logical-purity.
A more simple version :
filter_list(L,OutList):-findall(X, (select(X,L, L1),\+member(X, L1)) , OutList).
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
Without findall, you can try
filter_list(In, Out) :- filter_list(In, _, Out).
filter_list([], [], []).
filter_list([H|T], L1, L2) :-
filter_list(T, LL1, LL2),
( member(H, LL1)
-> L1 = LL1, L2 = LL2
; (select(H, LL2, L2)
-> L1 = [H|LL1]
; L1 = LL1, L2 = [H|LL2])).
without counting...
filter_uniques([],[]).
filter_uniques([H|T],F) :-
delete(T,H,D),
( D=T -> F=[H|R],S=T ; F=R,S=D ),
filter_uniques(S,R).
a more direct rewrite of your code, with library(yall) support for inlining of the filter predicate (the first argument to include/3)
filt_uniq(L,F) :-
include({L}/[E]>>aggregate(count,member(E,L),1),L,F).
A simple version:
(see the comment by #false below, the version with findall/3 has some inconsistency problems in more complex queries but second version looks ok however it is definitely not so efficient ).
filter_list(L,OutList):-findall(X, (member(X,L),count(X,L,N),N=:=1) , OutList).
count(_,[],0).
count(X,[X|T],N):-count(X,T,N1),N is N1+1.
count(X,[X1|T],N):-dif(X,X1),count(X,T,N).
The predicate filter_list/2 uses findall/3 and simply states find all X that belong to the list L and count returns 1 and store them in OutList.
Example:
?- filter_list([1,1,2,2,3,4,4,5],L).
L = [3, 5].
You could write filter_list/2 without using findall/3 like:
filter_list(L,OutList):- filter_list(L,OutList,L).
filter_list([],[],_).
filter_list([H|T],[H|T1],L):-count(H,L,N), N=:=1, filter_list(T,T1,L).
filter_list([H|T],T1,L):-count(H,L,N), N > 1, filter_list(T,T1,L).
I found a similar post but it didn't work for me. So please don't try to redirect me to other links.
This is the result that I want:
removeadj([a,a,a,b,c,c,a,a],[a,b,c,a]).
>True.
I tried this code:
concatener([],R,R).
concatener([X|Y],L,R):-concatener(Y,L,R1),R=[X|R1].
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),concatener([],R1,R).
removeadj([X|Y],R):- removeadj(Y,R1),concatener(X,R1,R).
When I try a list with one element duplicated many times, it works:
removeadj([a,a,a,a,a],[a]).
> True
But when I use different elements, it doesn't work:
removeadj([a,a,a,b],[a,b]).
> False.
I do not see where the problem is, so I can't fix it. Please I need your help.
Relational names
The first is to reconsider the name of your relation. Currently, it suggests that someone has to do something. removeadj that's a command. Quite an adequate name in a programming language where commands are the ruling metaphor. But not in Prolog.
In Prolog, we have relations. A name that reflects this relationness is often very helpful. Why not list_list/2? After all, your relation is about two lists! OK, maybe that name was a bit too general. What about list__list_without_adjacent_elements/2? Lengthy, but relational. Maybe we shorten that to: list_noadjs/2. Note the s at the end: That means: it's a plural which means it's a list.
Observe properties
Before thinking about "doing" this or that. Rather muse about concrete examples - preferably ground examples, as you have given them. And about other properties. One observation is that all elements of the second list will be there in the first. In fact not only that. But they will also occur in the same order. Let me try to formulate that. Of course, that observation is not sufficient to write an entire predicate. But here comes the cool thing in Prolog: We do not need to implement everything. We can start with gross generalizations that contain all what we want plus some more.
Start with a too general definition.
To show you the most extreme, let's try:
list_noadjs(_Xs, _Ys).
This is the mother of all binary relations! That definition always succeeds, no matter what. Evidently, we will have to specialize it. Say, by looking at the second argument which is a list:
list_noadjs(_Xs, []).
list_noadjs(_Xs, [Y|Ys]) :-
list_noadjs(_, Ys).
If the list is [] so will be the original one. And both start with the same element!
list_noadjs(Xs, []) :-
Xs = [].
list_noadjs(Xs, [Y|Ys]) :-
Xs = [Y|_],
list_noadjs(_, Ys).
or more compactly:
list_noadjs([], []).
list_noadjs([Y|_Xs], [Y|Ys]) :-
list_noadjs(_, Ys).
Now, the first list contains elements of the second list. And in between something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
list_(Xs0,Xs1),
list_noadjs(Xs1, Ys).
list_(Xs,Xs).
list_([_|Xs0],Xs) :-
list_(Xs0,Xs).
Is this already our relation? Let's give it a try:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aaa"
; Ys = "aab"
; Ys = "aa"
; Ys = "aab"
; Ys = "aa"
; Ys = "ab" % <===== * RRRIGHT !!!!***
; Ys = "a"
; false.
(Btw. I am using library(double_quotes) to make answers more readable.)
So we do have the expected solution. Alas, there are many incorrect solutions, too! We will have to continue to specialize this program:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
Now this is much better, but still not perfect:
?- list_noadjs("aaab",Ys).
Ys = "aaab"
; Ys = "aab"
; Ys = "aab"
; Ys = "ab" % !!! Right
; false.
We have to further specialize the program: After a sequence of identical elements, there must be something else:
list_noadjs([], []).
list_noadjs([Y|Xs0], [Y|Ys]) :-
eq_list_(Y, Xs0,Xs1),
nohead(Xs1, Y),
list_noadjs(Xs1, Ys).
eq_list_(_, Xs,Xs).
eq_list_(Y, [Y|Xs0],Xs) :-
eq_list_(Y, Xs0,Xs).
nohead([], _X).
nohead([X|_], Y) :-
dif(X, Y).
So that's our relation.
Enjoy the relation!
Seriously. Don't just use the test cases you had. You have now a relation! That's not a function in disguise, it is truly more than that. Try it out! Ask completely unusual things, like:
?- list_noadjs(Xs,"abc").
Xs = "abc"
; Xs = "abcc"
; Xs = "abccc"
; Xs = "abcccc"
; ... .
So here we ask: Which lists correspond to "abc"? Note that only c is repeated! All the other solutions are hidden behind this wall of infinity. But we can play a little trick to get them:
?- length(Xs,N), list_noadjs(Xs,"abc").
Xs = "abc", N = 3
; Xs = "abcc", N = 4
; Xs = "abbc", N = 4
; Xs = "aabc", N = 4
; Xs = "abccc", N = 5
; Xs = "abbcc", N = 5
; Xs = "abbbc", N = 5
; Xs = "aabcc", N = 5
; Xs = "aabbc", N = 5
; Xs = "aaabc", N = 5
; Xs = "abcccc", N = 6
; ... .
Don't shy away from non-termation.
We have already seen it: Very often, we get infinitely many solutions. And (I must admit) it's even worse: Sometimes, our relations do not even terminate.
Here is one such example. Say, is there a way that the list in the second argument contains duplicates? Or that the following holds:
?- list_noadjs(Xs,[X,X]).
loops.
Prolog answers: mumble, mumble, lemme see...
To master Prolog, you will have to understand this in detail. But for the moment, there is often a good way out:
Specialize queries to get termination
So instead of asking: Is there any term that may correspond to [X,X] we may ask: Is there a list of size 5 (or any other finite size). Now Prolog denies this:
?- Xs = [_,_,_,_,_], list_noadjs(Xs,[X,X]).
false.
That's not the universal answer you wanted. But it is better than nothing.
Sometimes all these queries are too much for you. Let Prolog do the thinking for you by:
Enumerating all solutions
Often, this is very simple. Start with the most general query. The big advantage here is that no thinking is required on your side at all. And still you look like a pro:
?- list_noadjs(Xs,Ys).
Xs = [], Ys = []
; Xs = [_A], Ys = [_A]
; Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_B,_C], Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B) ?
; Xs = [_A,_B,_C,_D], Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; Xs = [_A,_B,_C,_D,_E], Ys = [_A,_B,_C,_D,_E], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
What we got here are so called answers. A single answer may contain infinitely many solutions. Think of this: You are looking at infinity! Some conditions (called constraints) must hold, like dif/2. But that's it.
The third answer was:
Xs = [_A,_B], Ys = [_A,_B], dif(_B,_A)
So Xs and Ys are the same list with two distinct elements. So this answer implies Xs = "ab", Ys = "ab" but also Xs = [1,2], Ys = [1,2] and many, many more.
Even better, enumerate all answers in a systematic ("fair") manner:
?- length(Xs, N), list_noadjs(Xs,Ys).
Xs = [], N = 0, Ys = []
; Xs = [_A], N = 1, Ys = [_A]
; Xs = [_A,_B], N = 2, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A], N = 2, Ys = [_A]
; Xs = [_A,_B,_C], N = 3, Ys = [_A,_B,_C], dif(_B,_A), dif(_C,_B)
; Xs = [_A,_B,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_B], N = 3, Ys = [_A,_B], dif(_B,_A)
; Xs = [_A,_A,_A], N = 3, Ys = [_A]
; Xs = [_A,_B,_C,_D], N = 4, Ys = [_A,_B,_C,_D], dif(_B,_A), dif(_C,_B), dif(_D,_C)
; ... .
These are all solutions up to length 3. There are no other! We know this for sure because the last answer is already of size 4. So all solutions below are here already!
Often looking at such answers is very helpful. For example, it permits you to rapidly detect errors (like in the other answer that was given previously). So, don't tell nobody about that trick.
The last clause in the predicate still has an issue:
removeadj([X|Y], [X|R1]):- removeadj(Y, R1).
In the event that the element following X is also X, X is still carried in the head of the second argument. This clause must check if the following element is different before allowing it in the second argument:
removeadj([X,Y|L], [X|R]) :-
dif(X,Y),
removeadj([Y|L], R).
Here, Y is at the head of the second list only if it's different from X.
So the whole solution looks like:
removeadj([], []).
removeadj([X], [X]).
removeadj([X,X|T], R) :- % drop an X if next element is X
removeadj([X|T], R).
removeadj([X,Y|T], [X|R]) :- % carry X along if next element different
dif(X,Y),
removeadj([Y|T], R).
removeadj([],[]).
removeadj([X],[X]).
removeadj([X|Y],R):- Y=[X|L],removeadj(Y,R1),R=R1.
removeadj([X|Y],[X|R1]):- removeadj(Y,R1).
Given a list eg [1,2,3,7,2,5,8,9,3,4] how would I extract the sequences within the list?
A sequence is defined as an ordered list (Normally I would say n-tuple but I have been told in prolog a tuple is referred to as a sequence). So we want to cut the list at the point where the next element is smaller than the previous one.
So for the list [1,2,3,7,2,5,8,9,3,4] it should return:
[ [1,2,3,7], [2,5,8,9], [3,4] ] %ie we have cut the list at position 4 & 8.
For this exercise you CANNOT use the construct ; or ->
Many thanks in advance!
EXAMPLE RESULTS:
eg1.
?-function([1,2,3,7,2,5,8,9,3,4],X): %so we cut the list at position 4 & 9
X = [ [1,2,3,7], [2,5,8,9], [3,4] ].
eg2.
?-function([1,2,3,2,2,3,4,3],X): %so we cut the list at position 3,4 & 8
X = [ [1,2,3], [2], [2,3,4], [3] ].
Hopefully that helps clarify the problem. If you need further clarification just let me know! Thanks again in advance for any help you are able to provide.
First, let's break it down conceptually. The predicate list_ascending_rest/3 defines a relation between a list Xs, the left-most ascending sublist of maximum length Ys, and the remaining items Rest. We will use it like in the following query:
?- Xs = [1,2,3,7,2,5,8,9,3,4], list_ascending_rest(Xs,Ys,Rest).
Ys = [1,2,3,7],
Rest = [2,5,8,9,3,4] ;
false.
The straight-forward predicate definition goes like this:
:- use_module(library(clpfd)).
list_ascending_rest([],[],[]).
list_ascending_rest([A],[A],[]).
list_ascending_rest([A1,A2|As], [A1], [A2|As]) :-
A1 #>= A2.
list_ascending_rest([A1,A2|As], [A1|Bs], Cs) :-
A1 #< A2,
list_ascending_rest([A2|As], Bs,Cs).
Then, let's implement predicate list_ascendingParts/2. This predicate repeatedly uses list_ascending_rest/3 for each part until nothing is left.
list_ascendingParts([],[]).
list_ascendingParts([A|As],[Bs|Bss]) :-
list_ascending_rest([A|As],Bs,As0),
list_ascendingParts(As0,Bss).
Example queries:
?- list_ascendingParts([1,2,3,7,2,5,8,9,3,4],Xs).
Xs = [[1,2,3,7], [2,5,8,9], [3,4]] ;
false.
?- list_ascendingParts([1,2,3,2,2,3,4,3],Xs).
Xs = [[1,2,3], [2], [2,3,4], [3]] ;
false.
Edit 2015/04/05
What if the ascending parts are known but the list is unknown? Let's find out:
?- list_ascendingParts(Ls, [[3,4,5],[4],[2,7],[5,6],[6,8],[3]]).
Ls = [3,4,5,4,2,7,5,6,6,8,3] ? ;
no
And let's not forget about the most general query using list_ascendingParts/2:
?- assert(clpfd:full_answer).
yes
?- list_ascendingParts(Ls, Ps).
Ls = [], Ps = [] ? ;
Ls = [_A], Ps = [[_A]] ? ;
Ls = [_A,_B], Ps = [[_A],[_B]], _B#=<_A, _B in inf..sup, _A in inf..sup ? ...
Edit 2015-04-27
Room for improvement? Yes, definitely!
By using the meta-predicate splitlistIfAdj/3 one can "succeed deterministically" and "use non-determinism when required", depending on the situation.
splitlistIfAdj/3 is based on if_/3 as proposed by #false in this answer. So the predicate passed to it has to obey the same convention as (=)/3 and memberd_truth/3.
So let's define (#>)/3 and (#>=)/3:
#>=(X,Y,Truth) :- X #>= Y #<==> B, =(B,1,Truth).
#>( X,Y,Truth) :- X #> Y #<==> B, =(B,1,Truth).
Let's re-ask above queries, using splitlistIfAdj(#>=)
instead of list_ascendingParts:
?- splitlistIfAdj(#>=,[1,2,3,7,2,5,8,9,3,4],Pss).
Pss = [[1,2,3,7],[2,5,8,9],[3,4]]. % succeeds deterministically
?- splitlistIfAdj(#>=,[1,2,3,2,2,3,4,3],Pss).
Pss = [[1,2,3],[2],[2,3,4],[3]]. % succeeds deterministically
?- splitlistIfAdj(#>=,Ls,[[3,4,5],[4],[2,7],[5,6],[6,8],[3]]).
Ls = [3,4,5,4,2,7,5,6,6,8,3] ; % works the other way round, too
false. % universally terminates
Last, the most general query. I wonder what the answers look like:
?- splitlistIfAdj(#>=,Ls,Pss).
Ls = Pss, Pss = [] ;
Ls = [_G28], Pss = [[_G28]] ;
Ls = [_G84,_G87], Pss = [[_G84],[_G87]], _G84#>=_G87 ;
Ls = [_G45,_G48,_G41], Pss = [[_G45],[_G48],[_G41]], _G45#>=_G48, _G48#>=_G41
% and so on...
maplist/3 as suggested in the comment won't help you here because maplist/3 is good at taking a list and mapping each element into a same-size collection of something else, or establishing a relation evenly across all of the individual elements. In this problem, you are trying to gather contiguous sublists that have certain properties.
Here's a DCG solution. The idea here is to examine the list as a series of increasing sequences where, at the boundary between them, the last element of the prior sequence is less than or equal to the first element of the following sequence (as the problem statement basically indicates).
% A set of sequences is an increasing sequence ending in X
% followed by a set of sequences that starts with a value =< X
sequences([S|[[Y|T]|L]]) --> inc_seq(S, X), sequences([[Y|T]|L]), { X >= Y }.
sequences([S]) --> inc_seq(S, _).
sequences([]) --> [].
% An increasing sequence, where M is the maximum value
inc_seq([X,Y|T], M) --> [X], inc_seq([Y|T], M), { X < Y }.
inc_seq([X], X) --> [X].
partition(L, R) :- phrase(sequences(R), L).
| ?- partition([1,2,3,4,2,3,8,7], R).
R = [[1,2,3,4],[2,3,8],[7]] ? ;
(1 ms) no
| ?- partition([1,2,3,2,2,3,4,3],X).
X = [[1,2,3],[2],[2,3,4],[3]] ? ;
(1 ms) no
The only reason for the rule sequences([]) --> []. is if you want partition([], []) to be true. Otherwise, the rule isn't required.
I cannot figure out why the following query from the given Prolog code generates the error Out of local stack.
Prolog code:
likes(g,c).
likes(c,a).
likes(c,b).
likes(b,a).
likes(b,d).
likes(X,Z) :- likes(X,Y), likes(Y,Z).
the query
?- likes(g,X).
results in
X = c ;
X = a ;
X = b ;
ERROR: Out of local stack
Edit 1 This is the way I think that Prolog should deal with this query,
likes(g,c) is a fact, so X={c}
likes(g,b) <= likes(g,c) and likes(c,b), so X={c,b}
likes(g,a) <= likes(g,b) and likes(b,a), so X={c,b,a}
likes(g,d) <= likes(g,b) and likes(b,d), so X={c,b,a,d}
likes(g,a) and false, so nothing to add to X
likes(g,d) and false, so nothing to add to X
end of backtracking search.
Edit 2 I managed to get what I was looking for by the following modification of the code:
likes(g,c).
likes(c,a).
likes(c,b).
likes(b,a).
likes(b,d).
indirect_likes(A,B):- likes(A,B).
indirect_likes(A,C):- likes(B,C), indirect_likes(A,B).
the query
?- indirect_likes(g,Which).
results in
Which = c ;
Which = a ;
Which = b ;
Which = a ;
Which = d ;
false.
However, there is still something which I cannot figure out the rationale behind. If I change the last rule to be
indirect_likes(A,C):- indirect_likes(A,B), likes(B,C).
Then I get ERROR: Out of local stack! As far as I know, logical conjunction is commutative.
To get the transitive-closure of binary relation R_2, use meta-predicate closure/3 like so:
?- closure(R_2,From,To).
Let's run a sample query of closure/3 together with likes/2!
?- closure(likes,X,Y).
X = g, Y = c
; X = g, Y = a
; X = g, Y = b
; X = g, Y = a % redundant
; X = g, Y = d
; X = c, Y = a
; X = c, Y = b
; X = c, Y = a % redundant
; X = c, Y = d
; X = b, Y = a
; X = b, Y = d
; false. % query terminates universally
We get the same answers when we use indirect_likes/2, but in a different order:
?- indirect_likes(X,Y).
X = g, Y = c
; X = c, Y = a
; X = c, Y = b
; X = b, Y = a
; X = b, Y = d
; X = g, Y = a
; X = g, Y = b
; X = c, Y = a % redundant
; X = g, Y = a % redundant
; X = c, Y = d
; X = g, Y = d
; false. % query terminates universally
As you stated in your comments to #C.B.'s answer, binary relations are not necessarily reflexive and/or symmetric. With the definition you gave, likes/2 is neither:
?- likes(X,X).
false. % not reflexive (not even close)
?- likes(X,Y), likes(Y,X).
false. % not symmetric (not even close)
So far, so good!
Let's tentatively add the following additional fact to your database:
likes(b,b).
With this expanded definition, indirect_likes/2 behaves erratically:
?- indirect_likes(b,b).
true
; true
; true
... % does not terminate universally
?- indirect_likes(X,Y), false. % do we get finite failure?
... % no! query does not terminate universally
What can we do? Let's use meta-predicate closure/3 with the expanded version of likes/2!
?- closure(likes,b,b).
true % succeeds non-deterministically
; false. % query terminates universally
?- closure(likes,X,Y), false. % do we get finite failure?
false. % yes! query terminates universally
The bottom line?
In pure Prolog, conjunction is commutative, as far as the logical meaning is concerned.
Due to Prolog's SLD resolution, the goal false,repeat finitely fails, but repeat,false does not.
The programmer needs to take care of termination, but usually this is a small price to pay for the raw performance and control that Prolog offers.
In this answer I passed "termination worries" on to the implementor of meta-predicate closure/3 :)
You spin off into infinite recursion.
Once you get to likes(b,a), you call likes(a,_G4382), which has no fact defined so it switches to the rule likes(X,Z) :- likes(X,Y), likes(Y,Z). So it calls likes(a,_G4383) which calls likes(X,Z) :- likes(X,Y), likes(Y,Z). over and over and over.
You might want to define and auxillary predicate aux_likes/2 that hosts all your facts. This will only work if there are no circular relationships, i.e. aux_likes(b,c) and aux_likes(c,b). You would also need to define likes(X,X). This is essentially a graph problem and in graph theory a node has to be connected to itself. If you use it as a generator, it will go off into into infinite recursion (if you have cycles) unless you add cuts which are not ideal.
If using swi-prolog, feel free to enter the debug or spy query to see what's going on.
Code:
aux_likes(g,c).
aux_likes(c,a).
aux_likes(c,b).
aux_likes(b,a).
aux_likes(b,d).
likes(X,Z) :- aux_likes(X,Y), likes(Y,Z).
likes(X,X).
Output with new predicate:
?- likes(g,X),print(X),nl,fail.
a
a
d
b
c
g
false.
This says g can like a through c or through b. It likes d through b, it likes b through c and it likes c directly. It also must like itself inorder to query like this. If you would rather have the usage mode (+,+) meaning you supply it with both terms and no variables (as a checker) you can do
?- likes(g,c).
true .