Prolog (Sicstus) - nonmember and setof issues

141 views Asked by At

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!

2

There are 2 answers

2
CapelliC On BEST ANSWER

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'
1
Isabelle Newbie On

setof/3 is easier to use if you create an auxiliary predicate that expresses the relationship you are interested in:

disjoint_routes(W, Z) :-
    route(W, Stations1),
    route(Z, Stations2),
    disjoint(Stations1, Stations2).

With this, the definition of disjointed_lines/1 becomes shorter and simpler and no longer needs any ^ operators:

disjointed_lines(Ls) :-
    setof((W, Z), disjoint_routes(W, Z), Ls).

The variables you don't want in the result of setof/3 are automatically hidden inside the auxiliary predicate definition.