Prolog (SWI): Conjunction over a list? - list

Say I have a predicate eval(A) that just evaluates to true/false depending on some input
Now the thing is, I want another predicate
and(List, R)
that succeeds iff List is empty/the conjunction of the eval of every element in List is R. Where R is just true/false.
So if List had the items [a, b, c], then return [eval(a) ^ eval(b) ^ eval(c)] == R
My attempt:
and([], true).
and([H|T], R) :- eval(H), and(T, R).
and([H|T], R) :- eval(H) = R.
But i don't know how to compare to R properly.

Prolog has a relational data model, predicates don't carry values. You must add an argument to eval/1, holding the 'return value'.
and([], true).
and([H|T], R) :-
eval(H, X), % could 'shortcircuit' to false here if X is false
and(T, And),
( X == true, And == true -> R = true ; R = false ).
edit better could be, without changing eval/1:
and(L, R) :-
maplist(eval, L) -> R = true ; R = false.

Related

Extract other tuple member in Prolog

I want to write a predicate that takes a member of a tuple, that tuple, and outputs the other member of the tuple. You may assume tuples always have 2 elements, and the supplied member is always present.
extract_from_tuple(_, [], _).
extract_from_tuple(X, [H|T], R) :-
( X \= H -> R is X, ! ; X = H -> extract_from_tuple(X, T, R) ).
I tried to implement a simple if-else statement syntax I found on How to write IF ELSE conditions in Prolog.
So for example,
extract_from_tuple(a, [b,a], R).
should output b in the result variable R
Same should be for other way around:
extract_from_tuple(a, [a,b], R).
Only this time it 'should' hit the else statement and recursively call the predicate with the other element of the supplying list.
I think this problem is very simple and can be solved using just unification:
extract(X, [X,Y], Y).
extract(Y, [X,Y], X).
Examples:
?- extract(a, [b,a], R).
R = b.
?- extract(a, [a,b], R).
R = b ;
false.
To avoid spurious choice point, you can code extract/3 as:
extract_deterministic(Item, [First,Second], Rest) :-
( Item = First
-> Rest = Second
; Rest = First ).
Notice, however, this last version is less general than the first one! For example:
?- extract(X, [a,b], R). % two answers!
X = a,
R = b ;
X = b,
R = a.
?- extract_deterministic(X, [a,b], R). % only one answer!
X = a,
R = b.

Comparing lists and elements

I am trying to compare and retrieve values from corresponding lists. My Predicate is correspond_elements(V1, Xs, V2, Ys), where I expect the following behavior:
?- correspond_elements(a, [a,b,b,a], X, [1,2,3,4]).
X = 1;
X = 4.
Where V1 is checked in the first list Xs, and the corresponding values in Ys are given to V2 to return. So far I have this:
% checks if the argument is a list
is_list([]).
is_list([_|T]) :-
is_list(T).
% predicate
correspond_elements(V1, [X|Xs], V2, [Y|Ys]) :-
is_list([X|Xs]),
is_list([Y|Ys]),
( V1 == X ->
V2 is Y
;
correspond_elements(V1, Xs, V2, Ys)
).
Which only gets the first value:
?- correspond_elements(a, [a,b,b,a], X, [1,2,3,4]).
X = 1.
I know that once the -> clause becomes true, then anything after the ; is not executed. It is clear that my code will only get the first answer it finds for X and stop, but I am unsure of how to keep recursing through the list and return all the possible answers for X, even after the first answer has been found.
As you have found out yourself, the if-then-else of Prolog A -> B; C will check the condition A, and if true it will execute B (and not C). Otherwise it will execute C (and not B).
You however want to execute C as an additional option in case A is true. This can be done, by transforming:
foo(Some,Parameters) :-
A
-> B
; C.
into:
foo(Some,Parameters) :-
A,
B.
foo(Some,Parameters) :-
C.
Since now A works as a guard for B, but regardless whether A succeeds or fails, Prolog will backtrack and execute the second foo/2 clause.
If we remove the is_list(..) predicates (which are a bit verbose in my opinion), we can produce the predicate:
correspond_elements(X, [X|_], Y, [Y|_]).
correspond_elements(V1, [_|Xs], V2, [_|Ys]) :-
correspond_elements(V1, Xs, V2, Ys).
We do not have to write the condition V1 == X here, since we used unification in the head for this. Because we use unification, it is V1 = X (one equation sign), so that means we can use the predicate in a more multi-directional way.
Querying the elements in the second list X:
?- correspond_elements(a, [a,b,b,a], X, [1,2,3,4]).
X = 1 ;
X = 4 ;
false.
Querying all tuples A and X of both lists (some sort of "zip"):
?- correspond_elements(A, [a,b,b,a], X, [1,2,3,4]).
A = a,
X = 1 ;
A = b,
X = 2 ;
A = b,
X = 3 ;
A = a,
X = 4 ;
false.
Obtain the elements in the first list:
?- correspond_elements(A, [a,b,b,a], 1, [1,2,3,4]).
A = a ;
false.
Generate a list such that 1 is in the list:
?- correspond_elements(A, [a,b,b,a], 1, L).
A = a,
L = [1|_G1285] ;
A = b,
L = [_G1284, 1|_G1288] ;
A = b,
L = [_G1284, _G1287, 1|_G1291] ;
A = a,
L = [_G1284, _G1287, _G1290, 1|_G1294] .

Prolog , return boolean from consecutive recursive checks

Hello everyone,
The following code does recursive checks. For each call , F gets a value of either 1 or 0 , due to a condition . I want my test_liars predicate return True if all checks had result 1 , and False if at least one call , set F's value to 0.
What test_liars actually does , is not something really eager to explain , but I can if asked.
test_liars should return True to Flag's argument, asked :
test_liars(2,[3,2,1,4,2],[1,0,0,1,0],Flag)
given different list ,rather than [1,0,0,1,0], it must return False
test_liars(_,[],_,_) .
test_liars(N,[HF|TF],[HT|TT],Flag) :-
(HT == 0 -> ( N >= HF -> F = 1 ; F = 0)
; ( N < HF -> F = 1 ; F = 0)),
test_liars(N,TF,TT,Flag),
write(F),
(F == 0 -> Flag = 'True' ; Flag = 'False').
First of all, I think it is more elegant to transform the nested if-then-else structure into a predicate. For instance test_liar/4:
% test_liar(N,HT,HF,F).
test_liar(N,0,HF,1) :-
N >= HF,
!.
test_liar(N,1,HF,1) :-
N < HF,
!.
test_liar(_,_,_,0).
Which makes things easier. Now you can write:
test_liars(_,[],_,_).
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,Flag),
write(F),
(F == 0 -> Flag = 'True' ; Flag = 'False').
Nevertheless we are not there yet. Your Flag should return 'False' if at least one element is a liar. That means that in the base case, there are no liars, so we should return 'True':
test_liars(_,[],_,'True').
In the inductive case we thus have to construct some kind of "and", like:
custom_and('True',1,'True') :-
!.
custom_and(_,_,'False').
Now we only need to call this custom_and/3 on the outcome of the recursive test_liars and test_liar:
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,SubFlag),
write(F),
custom_and(SubFlag,F,Flag).
Or now the full code:
test_liar(N,0,HF,1) :-
N >= HF,
!.
test_liar(N,1,HF,1) :-
N < HF,
!.
test_liar(_,_,_,0).
custom_and('True',1,'True') :-
!.
custom_and(_,_,'False').
test_liars(_,[],_,'True').
test_liars(N,[HF|TF],[HT|TT],Flag) :-
test_liar(N,HT,HF,F),
test_liars(N,TF,TT,SubFlag),
write(F),
custom_and(SubFlag,F,Flag).

Add two more occurrences using prolog

I have a list [a, b, a, a, a, c, c]
and I need to add two more occurrences of each element.
The end result should look like this:
[a, a, a, b, b, b, a, a, a, a, a, c, c, c, c]
If I have an item on the list that is the same as the next item, then it keeps going until there is a new item, when it finds the new item, it adds two occurrences of the previous item then moves on.
This is my code so far, but I can't figure out how to add two...
dbl([], []).
dbl([X], [X,X]).
dbl([H|T], [H,H|T], [H,H|R]) :- dbl(T, R).
Your code looks a bit strange because the last rule takes three parameters. You only call the binary version, so no recursion will ever try to derive it.
You already had a good idea to look at the parts of the list, where elements change. So there are 4 cases:
1) Your list is empty.
2) You have exactly one element.
3) Your list starts with two equal elements.
4) Your list starts with two different elements.
Case 1 is not specified, so you might need to find a sensible choice for that. Case 2 is somehow similar to case 4, since the end of the list can be seen as a change in elements, where you need to append two copies, but then you are done. Case 3 is quite simple, we can just keep the element and recurse on the rest. Case 4 is where you need to insert the two copies again.
This means your code will look something like this:
% Case 1
dbl([],[]).
% Case 2
dbl([X],[X,X,X]).
% Case 3
dbl([X,X|Xs], [X|Ys]) :-
% [...] recursion skipping the leading X
% Case 4
dbl([X,Y|Xs], [X,X,X|Ys]) :-
dif(X,Y),
% [...] we inserted the copies, so recursion on [Y|Xs] and Ys
Case 3 should be easy to finish, we just drop the first X from both lists and recurse on dbl([X|Xs],Ys). Note that we implicitly made the first two elements equal (i.e. we unified them) by writing the same variable twice.
If you look at the head of case 4, you can directly imitate the pattern you described: supposed the list starts with X, then Y and they are different (dif(X,Y)), the X is repeated 3 times instead of just copied and we then continue with the recursion on the rest starting with Y: dbl([Y|Xs],Ys).
So let's try out the predicate:
?- dbl([a,b,a,a,a,c,c],[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]).
true ;
false.
Our test case is accepted (true) and we don't find more than one solution (false).
Let's see if we find a wrong solution:
?- dif(Xs,[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]), dbl([a,b,a,a,a,c,c],Xs).
false.
No, that's also good. What happens, if we have variables in our list?
?- dbl([a,X,a],Ys).
X = a,
Ys = [a, a, a, a, a] ;
Ys = [a, a, a, X, X, X, a, a, a],
dif(X, a),
dif(X, a) ;
false.
Either X = a, then Ys is single run of 5 as; or X is not equal to a, then we need to append the copies in all three runs. Looks also fine. (*)
Now lets see, what happens if we only specify the solution:
?- dbl(X,[a,a,a,b,b]).
false.
Right, a list with a run of only two bs can not be a result of our specification. So lets try to add one:
?- dbl(X,[a,a,a,b,b,b]).
X = [a, b] ;
false.
Hooray, it worked! So lets as a last test look what happens, if we just call our predicate with two variables:
?- dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G15],
Ys = [_G15, _G15, _G15] ;
Xs = [_G15, _G15],
Ys = [_G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15] ;
Xs = [_G15, _G15, _G15, _G15],
Ys = [_G15, _G15, _G15, _G15, _G15, _G15] ;
[...]
It seems we get the correct answers, but we see only cases for a single run. This is a result of prolog's search strategy(which i will not explain in here). But if we look at shorter lists before we generate longer ones, we can see all the solutions:
?- length(Xs,_), dbl(Xs,Ys).
Xs = Ys, Ys = [] ;
Xs = [_G16],
Ys = [_G16, _G16, _G16] ;
Xs = [_G16, _G16],
Ys = [_G16, _G16, _G16, _G16] ;
Xs = [_G86, _G89],
Ys = [_G86, _G86, _G86, _G89, _G89, _G89],
dif(_G86, _G89) ;
Xs = [_G16, _G16, _G16],
Ys = [_G16, _G16, _G16, _G16, _G16] ;
Xs = [_G188, _G188, _G194],
Ys = [_G188, _G188, _G188, _G188, _G194, _G194, _G194],
dif(_G188, _G194) ;
[...]
So it seems we have a working predicate (**), supposed you filled in the missing goals from the text :)
(*) A remark here: this case only works because we are using dif. The first predicates with equality, one usually encounters are =, == and their respective negations \= and \==. The = stands for unifyability (substituting variables in the arguments s.t. they become equal) and the == stands for syntactic equality (terms being exactly equal). E.g.:
?- f(X) = f(a).
X = a.
?- f(X) \= f(a).
false.
?- f(X) == f(a).
false.
?- f(X) \== f(a).
true.
This means, we can make f(X) equal to f(a), if we substitute X by a. This means if we ask if they can not be made equal (\=), we get the answer false. On the other hand, the two terms are not equal, so == returns false, and its negation \== answers true.
What this also means is that X \== Y is always true, so we can not use \== in our code. In contrast to that, dif waits until it can decide wether its arguments are equal or not. If this is still undecided after finding an answer, the "dif(X,a)" statements are printed.
(**) One last remark here: There is also a solution with the if-then-else construct (test -> goals_if_true; goals_if_false, which merges cases 3 and 4. Since i prefer this solution, you might need to look into the other version yourself.
TL;DR:
From a declarative point of view, the code sketched by #lambda.xy.x is perfect.
Its determinacy can be improved without sacrificing logical-purity.
Code variant #0: #lambda.xy.x's code
Here's the code we want to improve:
dbl0([], []).
dbl0([X], [X,X,X]).
dbl0([X,X|Xs], [X|Ys]) :-
dbl0([X|Xs], Ys).
dbl0([X,Y|Xs], [X,X,X|Ys]) :-
dif(X, Y),
dbl0([Y|Xs], Ys).
Consider the following query and the answer SWI-Prolog gives us:
?- dbl0([a],Xs).
Xs = [a,a,a] ;
false.
With ; false the SWI prolog-toplevel
indicates a choicepoint was left when proving the goal.
For the first answer, Prolog did not search the entire proof tree.
Instead, it replied "here's an answer, there may be more".
Then, when asked for more solutions, Prolog traversed the remaining branches of the proof tree but finds no more answers.
In other words: Prolog needs to think twice to prove something we knew all along!
So, how can we give determinacy hints to Prolog?
By utilizing:
control constructs !/0 and / or (->)/2 (potentially impure)
first argument indexing on the principal functor (never impure)
The code presented in the earlier answer by #CapelliC—which is based on !/0, (->)/2, and the meta-logical predicate (\=)/2—runs well if all arguments are sufficiently instantiated. If not, erratic answers may result—as #lambda.xy.x's comment shows.
Code variant #1: indexing
Indexing can improve determinacy without ever rendering the code non-monotonic. While different Prolog processors have distinct advanced indexing capabilities, the "first-argument principal-functor" indexing variant is widely available.
Principal? This is why executing the goal dbl0([a],Xs) leaves a choicepoint behind: Yes, the goal only matches one clause—dbl0([X],[X,X,X]).—but looking no deeper than the principal functor Prolog assumes that any of the last three clauses could eventually get used. Of course, we know better...
To tell Prolog we utilize principal-functor first-argument indexing:
dbl1([], []).
dbl1([E|Es], Xs) :-
dbl1_(Es, Xs, E).
dbl1_([], [E,E,E], E).
dbl1_([E|Es], [E|Xs], E) :-
dbl1_(Es, Xs, E).
dbl1_([E|Es], [E0,E0,E0|Xs], E0) :-
dif(E0, E),
dbl1_(Es, Xs, E).
Better? Somewhat, but determinacy could be better still...
Code variant #2: indexing on reified term equality
To make Prolog see that the two recursive clauses of dbl1_/3 are mutually exclusive (in certain cases), we reify the truth value of
term equality and then index on that value:
This is where reified term equality (=)/3 comes into play:
dbl2([], []).
dbl2([E|Es], Xs) :-
dbl2_(Es, Xs, E).
dbl2_([], [E,E,E], E).
dbl2_([E|Es], Xs, E0) :-
=(E0, E, T),
t_dbl2_(T, Xs, E0, E, Es).
t_dbl2_(true, [E|Xs], _, E, Es) :-
dbl2_(Es, Xs, E).
t_dbl2_(false, [E0,E0,E0|Xs], E0, E, Es) :-
dbl2_(Es, Xs, E).
Sample queries using SWI-Prolog:
?- dbl0([a],Xs).
Xs = [a, a, a] ;
false.
?- dbl1([a],Xs).
Xs = [a, a, a].
?- dbl2([a],Xs).
Xs = [a, a, a].
?- dbl0([a,b,b],Xs).
Xs = [a, a, a, b, b, b, b] ;
false.
?- dbl1([a,b,b],Xs).
Xs = [a, a, a, b, b, b, b] ;
false.
?- dbl2([a,b,b],Xs).
Xs = [a, a, a, b, b, b, b].
To make above code more compact, use control construct if_/3 .
I was just about to throw this version with if_/3 and (=)/3 in the hat when I saw #repeat already suggested it. So this is essentially the more compact version as outlined by #repeat:
list_dbl([],[]).
list_dbl([X],[X,X,X]).
list_dbl([A,B|Xs],DBL) :-
if_(A=B,DBL=[A,B|Ys],DBL=[A,A,A,B|Ys]),
list_dbl([B|Xs],[B|Ys]).
It yields the same results as dbl2/2 by #repeat:
?- list_dbl([a],DBL).
DBL = [a,a,a]
?- list_dbl([a,b,b],DBL).
DBL = [a,a,a,b,b,b,b]
The example query by the OP works as expected:
?- list_dbl([a,b,a,a,a,c,c],DBL).
DBL = [a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]
Plus here are some of the example queries provided by #lambda.xy.x. They yield the same results as #repeat's dbl/2 and #lambda.xy.x's dbl/2:
?- dif(Xs,[a,a,a,b,b,b,a,a,a,a,a,c,c,c,c]), list_dbl([a,b,a,a,a,c,c],Xs).
no
?- list_dbl(X,[a,a,a,b,b]).
no
?- list_dbl(L,[a,a,a,b,b,b]).
L = [a,b] ? ;
no
?- list_dbl(L,DBL).
DBL = L = [] ? ;
DBL = [_A,_A,_A],
L = [_A] ? ;
DBL = [_A,_A,_A,_A],
L = [_A,_A] ? ;
DBL = [_A,_A,_A,_A,_A],
L = [_A,_A,_A] ? ;
...
?- list_dbl([a,X,a],DBL).
DBL = [a,a,a,a,a],
X = a ? ;
DBL = [a,a,a,X,X,X,a,a,a],
dif(X,a),
dif(a,X)
?- length(L,_), list_dbl(L,DBL).
DBL = L = [] ? ;
DBL = [_A,_A,_A],
L = [_A] ? ;
DBL = [_A,_A,_A,_A],
L = [_A,_A] ? ;
DBL = [_A,_A,_A,_B,_B,_B],
L = [_A,_B],
dif(_A,_B) ? ;
DBL = [_A,_A,_A,_A,_A],
L = [_A,_A,_A] ?
dbl([X,Y|T], [X,X,X|R]) :- X \= Y, !, dbl([Y|T], R).
dbl([H|T], R) :-
T = []
-> R = [H,H,H]
; R = [H|Q], dbl(T, Q).
The first clause handles the basic requirement, adding two elements on sequence change.
The second one handles list termination as a sequence change, otherwise, does a plain copy.

How to predicate all pairs in a given list in Prolog?

When given a list I would like to compute all the possible combinations of pairs in the list.
e.g 2) input is a list (a,b,c) I would like to obtain pairs (a,b) (a,c) (b,c)
e.g 1) input is a list (a,b,c,d) I would like to obtain pairs (a,b) (a,c) (a,d) (b,c) (b,d) and (c,d)
Using select/3 twice (or select/3 once and member/2 once) will allow you to achieve what you want here. I'll let you work out the details and ask for help if it's still troublesome.
BTW, Prolog syntax for list isn't (a, b, c) but [a, b, c] (well, it's syntactic sugar but I'll leave it at that).
edit: as #WillNess pointed out, you're not looking for any pair (X, Y) but only for pairs where X is before Y in the list.
DCGs are a really good fit: as #false described, they can produce a graphically appealing solution:
... --> [] | [_], ... .
pair(L, X-Y) :-
phrase((..., [X], ..., [Y], ...), L).
Alternatively, if you use SWI-Prolog, a call to append/2 does the trick too, in a similar manner, but is less efficient than DCGs:
pair2(L, X-Y) :-
append([_, [X], _, [Y], _], L).
You can do it with a basic recursion, as #WillNess suggested in his comment. I'll leave this part for him to detail if needed!
OK, so to translate the Haskell definition
pairs (x:xs) = [ (x,y) | y <- xs ]
++ pairs xs
pairs [] = []
(which means, pairs in the list with head x and tail xs are all the pairs (x,y) where y is in xs, and also the pairs in xs; and there's no pairs in an empty list)
as a backtracking Prolog predicate, producing the pairs one by one on each redo, it's a straightforward one-to-one re-write of the above,
pair( [X|XS], X-Y) :- member( ... , XS) % fill in
; pair( XS, ... ). % the blanks
%% pair( [], _) :- false.
To get all the possible pairs, use findall:
pairs( L, PS) :- findall( P, pair( L, P), PS).
Consider using bagof if your lists can contain logical variables in them. Controlling bagof's backtracking could be an intricate issue though.
pairs can also be written as a (nearly) deterministic, non-backtracking, recursive definition, constructing its output list through an accumulator parameter as a functional programming language would do -- here in the top-down manner, which is what Prolog so excels in:
pairs( [X|T], PS) :- T = [_|_], pairs( X, T, T, PS, []).
pairs( [_], []).
pairs( [], []).
pairs( _, [], [], Z, Z).
pairs( _, [], [X|T], PS, Z) :- pairs( X, T, T, PS, Z).
pairs( X, [Y|T], R, [X-Y|PS], Z) :- pairs( X, T, R, PS, Z).
Here is a possible way of solving this.
The following predicate combine/3 is true
if the third argument corresponds to a list
contains pairs, where the first element of each pair
is equal to the first argument of combine/3.
The second element of each pair will correspond to an item
of the list in the second argument of the predicate combine/3.
Some examples how combine/3 should work:
?- combine(a,[b],X).
X = [pair(a,b)]
?- combine(a,[b,c,d],X).
X = [pair(a,b), pair(a,c), pair(a,d)]
Possible way of defining combine/3:
combine(A,[B],[pair(A,B)]) :- !.
combine(A,[B|T],C) :-
combine(A,T,C2), % Create pairs for remaining elements in T.
append([pair(A,B)],C2,C). % Append current pair and remaining pairs C2.
% The result of append is C.
Now combine/3 can be used to define pair/2:
pairs([],[]). % Empty list will correspond to empty list of pairs.
pairs([H|T],P) :- % In case there is at least one element.
nonvar([H|T]), % In this case it expected that [H|T] is instantiated.
pairs(H,T,P).
pairs(A,[B],[pair(A,B)]) % If remaining list contains exactly one element,
:- !. % then there will be only one pair(A,B).
pairs(A,[B|T],P) :- % In case there are at least two elements.
combine(A,[B|T],P2), % For each element in [B|T] compute pairs
% where first element of each pair will be A.
pairs(B,T,P3), % Compute all pairs without A recursively.
append(P2,P3,P). % Append results P2 and P3 together.
Sample usage:
?- pairs([a,b,c],X).
X = [pair(a, b), pair(a, c), pair(b, c)].
?- pairs([a,b,c,d],X).
X = [pair(a, b), pair(a, c), pair(a, d), pair(b, c), pair(b, d), pair(c, d)].
You can use append/ to iterate through the list:
?- append(_,[X|R],[a,b,c,d]).
X = a,
R = [b, c, d] ;
X = b,
R = [c, d] ;
X = c,
R = [d] ;
X = d,
R = [] ;
false.
Next, use member/2 to form a pair X-Y, for each Y in R:
?- append(_,[X|R],[a,b,c,d]), member(Y,R), Pair=(X-Y).
X = a,
R = [b, c, d],
Y = b,
Pair = a-b ;
X = a,
R = [b, c, d],
Y = c,
Pair = a-c ;
X = a,
R = [b, c, d],
Y = d,
Pair = a-d ;
X = b,
R = [c, d],
Y = c,
Pair = b-c ;
X = b,
R = [c, d],
Y = d,
Pair = b-d ;
X = c,
R = [d],
Y = d,
Pair = c-d ;
false.
Then, use findall/3 to collect all pairs in a list:
?- findall(X-Y, (append(_,[X|R],[a,b,c,d]), member(Y,R)), Pairs).
Pairs = [a-b, a-c, a-d, b-c, b-d, c-d].
Thus, your final solution can be expressed as:
pairs(List, Pairs) :-
findall(X-Y, (append(_,[X|R],List), member(Y,R)), Pairs).
An example of use is:
?- pairs([a,b,c,d], Pairs).
Pairs = [a-b, a-c, a-d, b-c, b-d, c-d].