Search code examples
prolog

How to access unique pairs from a list in Prolog?


I have a list L = [a, b, c] and I want to pick all unique non-repeating pairs: [a, b], [a, c], [b, c]. If I simply use the member function, it, of course, picks every permutation, so I have to have a predicate

pick_pair(List, X, Y) :-
    member(X, List),
    member(Y, List),
    \+ X = Y.

And to access the members I gather all the pairs into a list by using another predicate

unique_pairs(List, Result) :-
    findall([X, Y], pick_pair(List, X, Y), Result).

and only then I acess the resulting list, but it generates [[a, b], [a, c], [b, a], [b, c], [c, a], [c, b]]. I tried to get rid of the pairs that are just a reverse of pairs that were already there by list_to_set but [a, b] and [b, a] do not unify on default, so they're considered to be not equal and therefore belong to the set. I would somehow need to overload the unification for that function or something like that.

My question is:

Can we just access pairs in a list? Something like my_pairs(X, Y, L) which would assign the pair elements directly to X and Y. And if there is no such predicate, how can we make a list of the unique pairs so that we can access its elements by using member(X, List)?

The problem is, by the way, equivalent to getting all the combinations of length two.


Solution

  • Can use a custom variant of select:

    unique_pairs(L, E1, E2) :-
        select_forward(E1, L, L0),
        member(E2, L0).
        
    select_forward(E, [H|T], F) :-
        select_forward_(T, H, E, F).
        
    select_forward_(T, H, H, T).
    select_forward_([H|T], _, E, F) :-
        select_forward_(T, H, E, F).
    

    Result in swi-prolog:

    ?- unique_pairs([a,b,c], E1, E2).
    E1 = a,
    E2 = b ;
    E1 = a,
    E2 = c ;
    E1 = b,
    E2 = c ;
    false.