1

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?

repeat
  • 18,496
  • 4
  • 54
  • 166

3 Answers3

3
?- 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.
false
  • 10,264
  • 13
  • 101
  • 209
2

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.

Community
  • 1
  • 1
Tudor Berariu
  • 4,910
  • 2
  • 18
  • 29
0

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
       ).
    
Tudor Berariu
  • 4,910
  • 2
  • 18
  • 29
  • This will break the program catastrophically if you happen to have a `sol/1` predicate in your database, don't you think? –  Dec 11 '14 at 11:32
  • Of course, but predicates don't appear by magic. One might know if `sol/1` is used in the program. Anyway, one can use `gensym/2` if it's not sure about. – Tudor Berariu Dec 11 '14 at 11:37
  • `gensym/2` is not a solution, unless _each_ predicate you ever put in your database has been generated like that. –  Dec 11 '14 at 14:58
  • `repeat, gensym(sol, P), \+ current_predicate(P/1),!.` – Tudor Berariu Dec 11 '14 at 15:24
  • Anyway, the problem is not with predicates in the database. Name collisions with those can be avoided with the above code. Problems might arise from having conditions in some clauses that use those predicates that are asserted during the execution. – Tudor Berariu Dec 11 '14 at 15:41