Prolog: How to eliminate redundant answers?

301 views Asked by At

I have the map of Romania from the Russell and Norvig book about artificial intelligence. I created links between the cities like this:

link(oradea, zerind, 71).
link(oradea, sibiu, 151).
link(zerind, arad, 75).
link(arad, sibiu, 140).
link(arad, timisoara, 118).
link(timisoara, lugoj, 111).
link(lugoj, mehadia, 70).
link(mehadia, drobeta, 75).
link(drobeta, craiova, 120).

I want to find the cities that connect with Oradea or Arad but when I ask this:

(link(X, arad, _); link(arad, X, _));(link(X, oradea, _); link(oradea, X, _)).

It returns:

X = zerind ;
X = sibiu ;
X = timisoara ;
X = zerind ;
X = sibiu.

How can I make it return a solution only once?

3

There are 3 answers

5
Tudor Berariu On

Another way to get your solutions only once is to dynamically use the database to store the solutions when you find them:

?- retractall(sol(_)),
   (link(X,arad,_) ; link(arad,X,_) ; link(X,oradea,_) ; link(oradea,X,_)),
   \+ sol(X),
   assertz(sol(X)).

Observations:

  1. The solution with setof/3 is better and it should be preferred.

  2. In order to avoid leaving the database with garbage facts (undesired side effects), you can clean them all in the end:

    ?- ( 
         retractall(sol(_)),
         (link(X,arad,_);link(arad, X, _) ; link(X,oradea,_);link(oradea,X,_)),
         \+ sol(X), assertz(sol(X))
       ;
         retractall(sol(_)), fail
       ).
    
0
false On
?- setof(t, Dist^((link(X, arad, Dist) ; link(arad, X, Dist)) ;
                  (link(X, oradea, Dist) ; link(oradea, X, Dist))), _).
   X = sibiu
;  X = timisoara
;  X = zerind.
0
Tudor Berariu On

One easy way to achieve this is using setof/3 which eliminates the duplicates from the list of solutions:

?- setof(X, Dist^((link(X, arad, Dist) ; link(arad, X, Dist)) ; 
                  (link(X, oradea, Dist) ; link(orade, X, Dist))), All).

There is a difference between this and your query, as in this one all solutions are being put in a list, not given one at a time. But you can use member/2 to get the same behaviour:

?- setof(X, Dist^((link(X, arad, Dist) ; link(arad, X, Dist)) ;
                  (link(X, oradea, Dist) ; link(oradea, X, Dist))), All),
   member(X, All).
X = sibiu,
All = [sibiu, timisoara, zerind] ;
X = timisoara,
All = [sibiu, timisoara, zerind] ;
X = zerind,
All = [sibiu, timisoara, zerind].

Edit : false's answer is a better solution as it doesn't build the unnecessary list All.