In looking at the original code I gave, it was doing a depth first search for each complete path, then reducing the paths to a list of sorted unique nodes. While that works, it is very inefficient because it visits the same node multiple times.
Here is some code that does it a different way and I am guessing is more what you seek.
First is next/2
which given a node returns only its neighbors in a sorted list. While this can be used, I gave it so that you could understand how findall/3 works.
next/2
next(Start,Sorted_next) :-
findall(Next,arc(Start,Next),Next_list),
sort(Next_list,Sorted_next).
:- begin_tests(next).
test(1) :-
next(a,Next_list),
assertion( Next_list == [l,o] ).
test(2) :-
next(o,Next_list),
assertion( Next_list == [] ).
test(3) :-
next(l,Next_list),
assertion( Next_list == [v] ).
test(4) :-
next(v,Next_list),
assertion( Next_list == [l] ).
test(5) :-
next(y,Next_list),
assertion( Next_list == [o,v] ).
:- end_tests(next).
Second is closure/3
that is the transitive closure. The code was given in a StackOverflow question by a very reputable Prolog programmer so I did not change it. R_2
is the name of the facts that represents the edges (arc/2
).
closure/3
closure0(R_2, X0,X) :-
closure0(R_2, X0,X, [X0]).
closure(R_2, X0,X) :-
call(R_2, X0,X1),
closure0(R_2, X1,X, [X1,X0]).
closure0(_R_2, X,X, _).
closure0(R_2, X0,X, Xs) :-
call(R_2, X0,X1),
non_member(X1, Xs),
closure0(R_2, X1,X, [X1|Xs]).
non_member(_E, []).
non_member(E, [X|Xs]) :-
dif(E,X),
non_member(E, Xs).
:- begin_tests(closure).
test(1) :-
findall(Closure,closure(arc,a,Closure),Closures),
assertion( Closures == [o,l,v] ).
test(2) :-
findall(Closure,closure(arc,o,Closure),Closures),
assertion( Closures == [] ).
test(3) :-
findall(Closure,closure(arc,l,Closure),Closures),
assertion( Closures == [v] ).
test(4) :-
findall(Closure,closure(arc,v,Closure),Closures),
assertion( Closures == [l] ).
test(5) :-
findall(Closure,closure(arc,y,Closure),Closures),
assertion( Closures == [v,l,o] ).
:- end_tests(closure).
The only reason for using sort/2 is to normalize the results so that the test cases are easier to write, in other words I don’t have to check every ordering of the values in a list.