Search code examples
prologrestriction

Prolog: Filtering a list of goals and keeping the successful goals


Giving the following facts and restriction :

car(red, 2000).
car(black, 1990).
car(blue, 2010).

millennials(car(_,Y)) :- (car(_,Y)), Y =< 1995.
red(car(C,_)) :- car(C,_), C == red.

Given a list [millennials, red], I would like to make a call such as:

checkRestriction([millennials, red], car(red, 2000) , L).
L = red

Which return a list of the conditions that are respected by the car passed as argument.

This is different from this thread: How to filter a list in Prolog with multiple conditions?, because all restriction don't have to be successful, the goal is only to return a list of successful restriction with the car passed as argument.

I tried the following:

checkRestriction([],_,[]).
checkRestriction([H|T], Car, List) :-
   (    H(Car) 
   ->   append(L, H(Car), List), 
        checkRestriction(T, Car, List) 
   ;    checkRestriction(T, Car, List) ).

Which check is the restriction returns true with car as argument, and append in that case to a list the restriction itself, otherwise is simply call the predicate with the tail of the list, but I get compilation error.


Solution

  • The goal H(Car) causes a compilation error because a variable cannot be used as predicate name in Prolog. To correct your code, you must construct goal terms as follows:

    ?- H = red, Car = car(red,2000), Goal =.. [H,Car].
    H = red,
    Car = car(red, 2000),
    Goal = red(car(red, 2000)).
    
    ?- H = millennials, Car = car(red,2000), Goal =.. [H,Car].
    H = millennials,
    Car = car(red, 2000),
    Goal = millennials(car(red, 2000)).
    

    Thus, the correct code is:

    checkRestriction([], _, []).
    checkRestriction([H|T], Car, Result) :-
        Goal =.. [H, Car],
        (   call(Goal)
        ->  Result = [H|List],
            checkRestriction(T, Car, List)
        ;   checkRestriction(T, Car, Result) ).
    
    millennials(car(_,Y)) :- car(_,Y), Y =< 1995.
    
    red(car(C,_)) :- car(C, _), C == red.
    blue(car(C,_)) :- car(C, _), C == blue.
    black(car(C,_)) :- car(C, _), C == black.
    

    Running examples:

    ?- checkRestriction([millennials, red], car(red,2000), L).
    L = [red].
    
    ?- checkRestriction([millennials, black], car(black,1990), L).
    L = [millennials, black].
    
    ?- checkRestriction([millennials, blue], car(blue,2010), L).
    L = [blue].
    
    ?- checkRestriction([millennials, black], car(blue,2010), L).
    L = [].
    

    Alternatively, you can use call/N:

    checkRestriction([], _, []).
    checkRestriction([H|T], Car, Result) :-
        (   call(H, Car)
        ->  Result = [H|List],
            checkRestriction(T, Car, List)
        ;   checkRestriction(T, Car, Result) ).