prologprolog-setof

Prolog (Sicstus) - nonmember and setof issues


Given following facts:

route(TubeLine, ListOfStations).

route(green, [a,b,c,d,e,f]).
route(blue, [g,b,c,h,i,j]).
...

I am required to find all the pairs of tube Lines that do not have any stations in common, producing the following:

| ?- disjointed_lines(Ls).
Ls = [(yellow,blue),(yellow,green),(yellow,red),(yellow,silver)] ? ;
no

I came up with the below answer, however it does not only give me incorrect answer, but it also does not apply my X^ condition - i.e. it still prints results per member of Stations lists separately:

disjointed_lines(Ls) :- 
                        route(W, Stations1),
                        route(Z, Stations2),
                        setof(
                        (W,Z),X^
                        (member(X, Stations1),nonmember(X, Stations2)),
                         Ls).

This is the output that the definition produces:

| ?- disjointed_lines(L).
L = [(green,green)] ? ;
L = [(green,blue)] ? ;
L = [(green,silver)] ? ;
...

I believe that my logic relating to membership is incorrect, however I cannot figure out what is wrong. Can anyone see where am I failing?

I also read Learn Prolog Now chapter 11 on results gathering as suggested here, however it seems that I am still unable to use the ^ operator correctly. Any help would be appreciated!


UPDATE:

As suggested by user CapelliC, I changed the code into the following:

disjointed_lines(Ls) :- 
                        setof(
                        (W,Z),(Stations1, Stations2)^
                        ((route(W, Stations1),
                        route(Z, Stations2),notMembers(Stations1,Stations2))),
                         Ls).

notMembers([],_).
notMembers([H|T],L):- notMembers(T,L), nonmember(H,L).

The following, however, gives me duplicates of (X,Y) and (Y,X), but the next step will be to remove those in a separate rule. Thank you for the help!


Solution

  • I think you should put route/2 calls inside setof' goal, and express disjointness more clearly, so you can test it separately. About the ^ operator, it requests a variable to be universally quantified in goal scope. Maybe a concise explanation like that found at bagof/3 manual page will help...

    disjointed_lines(Ls) :- 
      setof((W,Z), Stations1^Stations2^(
        route(W, Stations1),
        route(Z, Stations2),
        disjoint(Stations1, Stations2)
      ), Ls).
    
    disjoint(Stations1, Stations2) :-
      ... % could be easy as intersection(Stations1, Stations2, [])
          % or something more efficient: early fail at first shared 'station'