How to use Prolog's foreach/2 loop

157 Views Asked by At

I am trying to get the foreach/2 loop to work in Prolog (using tkeclipse). I understand it needs an element in the first argument and a list to search for that element within as a second argument.

My problem is that I haven't been able to make it work despite trying to rewrite my code and looking for solutions online.

In the following code, I try to write a predicate visite/2 that takes a list of museums and returns a list of cities where they are located.

Your help would be really appreciated as it would allow me to be better prepared for my upcoming exam.

musee(paris,louvre).
musee(rome,vatican).
musee(madrid,prado).
musee(berlin,kulturforum).
musee(londres,british_museum).

visite([X],L) :-
  findall(V,musee(V,_),List),
  (
    foreach(X,List) do findall(C,musee(C,X),L)
  ).
2

There are 2 best solutions below

0
On

This isn't Prolog (at least, not as I know it).

And if it were, the notion of an iterative loop is antithetic to Prolog.

Your problem statement:

I [am trying to write] a predicate visite/2 that takes a list of museums and returns a list of cities where they are located

could be better done in a way more idiomatic to Prolog. Something like this:

musee( paris   , louvre         ).
musee( rome    , vatican        ).
musee( madrid  , prado          ).
musee( berlin  , kulturforum    ).
musee( londres , british_museum ).

%
% Accepts a list of museums and returns the list of cities (villes) in which they are found.
% Or... vice versa.
% 
% We invoke a helper predicate that will prevent problems if/when the predicate
% is used in a generative manner.
visite( Ms,Vs ) :- visite( Ms, [], Vs ).

visite( []     , _ , []     ) .
visite( [M|Ms] , T , [V|Vs] ) :-
  musee(V,M) ,
  \+ member( M:V, T ),
  !,
  visite( Ms, [M:V|T], Vs )
  .


0
On

ECLiPSe's do-loops are a shorthand for writing iterative recursions in a concise way. A simple application to your example would be

visite(Ms, Cs) :-
    ( foreach(M,Ms), foreach(C,Cs) do
        musee(C, M)
    ).

which is (modulo nondeterminism) equivalent to the explicit recursive form

visite([], []).
visite([M|Ms], [C|Cs]) :-
    musee(C, M),
    visite(Ms, Cs).

An algorithmically completely different way to solve your problem is using backtracking and findall, as in

visite(Ms, Cs) :-
    findall(C, (member(M, Ms), musee(C, M)), Cs).

Both are useful Prolog programming patterns.