简体   繁体   中英

Prolog recursive predicate returns to initial state after ending

I'm new to Prolog and I'm going through some trouble to understand what's wrong with my code.

I'm trying to make a food recommender. Basically, the user enters whether they want breakfast, lunch or dessert and a list of their tastes. The program then counts the number of matches between their tastes and the characteristics of every food and returns the food with the most matches.

I'm using recursion to go through the list of food and everything works as intended until it reaches the end with the empty list of food and then starts to fill it again, returning to the initial state of the problem. I'm not understanding why is the program doing this and I can't find a solution.

recommend(breakfast, Tastes, Result) :-
    findall(Breakfast, breakfast(Breakfast), Breakfasts),
    getBestMatch(Breakfasts, Tastes, Result, -1).

recommend(lunch, Tastes, Result) :-
    findall(Lunch, lunch(Lunch), Lunches),
    getBestMatch(Lunches, Tastes, Result, -1).

recommend(dessert, Tastes, Result) :-
    findall(Dessert, dessert(Dessert), Desserts),
    getBestMatch(Desserts, Tastes, Result, -1).


% Recursive predicate. Goes through the list of food and calls the matches predicate to count the
% matches and compares it to the current best match. When the food list is empty, it should stop. 
% Result should be the name of the best matching food. I think here's where the error is. 

getBestMatch([], _, _, _).
getBestMatch([H|T], Tastes, Result, BestMatch) :-
    matches(H, Tastes, Matches),
    Matches > BestMatch
    -> getBestMatch(T, Tastes, H, Matches)
    ; getBestMatch(T, Tastes, Result, BestMatch).


% Counts the number of matches between the food's characteristics and the tastes.

matches(Food, Tastes, Matches) :-
    characteristics(Food, Characteristics),
    intersection(Tastes, Characteristics, MatchList),
    length(MatchList, Matches).

% These are some examples of how I have declared the food and its characteristics

lunch(lasagna).
lunch(pizza).
dessert(cake).

characteristics(lasagna, [warm, salty, pasta, italian, meat]).
characteristics(pizza, [warm, salty, cheese, italian]).
characteristics(cake, [cold, sweet, vegetarian]).

And here's a fragment of the trace.

   Call: (12) getBestMatch([pizza], [italian, cheese], lasagna, 1) ? creep
   Call: (13) matches(pizza, [italian, cheese], _4220) ? creep
   Call: (14) characteristics(pizza, _4262) ? creep
   Exit: (14) characteristics(pizza, [warm, salty, cheese, italian]) ? creep
   Call: (14) lists:intersection([italian, cheese], [warm, salty, cheese, italian], _4376) ? creep
   Exit: (14) lists:intersection([italian, cheese], [warm, salty, cheese, italian], [italian, cheese]) ? creep
   Call: (14) length([italian, cheese], _4474) ? creep
   Exit: (14) length([italian, cheese], 2) ? creep
   Exit: (13) matches(pizza, [italian, cheese], 2) ? creep
   Call: (13) 2>1 ? creep
   Exit: (13) 2>1 ? creep
   Call: (13) getBestMatch([], [italian, cheese], pizza, 2) ? creep
   Exit: (13) getBestMatch([], [italian, cheese], pizza, 2) ? creep
   Exit: (12) getBestMatch([pizza], [italian, cheese], lasagna, 1) ? creep
   Exit: (11) getBestMatch([lasagna, pizza], [italian, cheese], _2882, -1) ? creep
   Exit: (10) recommend(lunch, [italian, cheese], _2882) ? creep
true.

I've tried looking through similar questions but none of them works for me.

PS: Sorry if this question or the code sample is too large, this was my first question here and I couldn't figure out how to simplify it more.

There is something mixed up in your getBestMatch/4 predicate. Let's go through the roles of the atributes:

  1. Attribute [H|T] seems to be a list of dishes to choose from (Input).
  2. Attribute Tastes seems to be a list of favored properties (Input).
  3. Attribute Result seems to be the favored dish from the List (Output).
  4. Attribute BestMatch seems to be the best match value (Input? Better to be an output).

You have an if-then-else in this clause which reads as pseudocode:

if matches(H, Tastes, Matches) and if Matches > BestMatch
then getBestMatch(T, Tastes, H, Matches)
else getBestMatch(T, Tastes, Result, BestMatch)

There are some issues here, like connecting values of attributes to each other. Let's try to resolve it. At first when to stop: if your List is empty, return a -1 as BestMatch

getBestMatch([], _, _, -1).

Now before doing anything you need to know what was the BestMatch value from the Tail List T is to compare you current element H with it:

getBestMatch([H|T], Tastes, Result, BestMatch) :-
    getBestMatch(T, Tastes, TmpRes, TmpBest),

Assuming the not-yet-programmed code works, you know now the recommendation from the TmpRes with score TmpBest . Now you need to know the score of your current dish H .

    matches(H, Tastes, Matches),

And then there follows an if-then-else for tunneling the right result to the "output"

    (   Matches > TmpBest
    ->  Result = H,
        BestMatch = Matches
    ;   Result = TmpRes,
        BestMatch = TmpBest
    ).

Or as complete code for getBestMatch/4 :

getBestMatch([], _, _, -1).
getBestMatch([H|T], Tastes, Result, BestMatch) :-
    getBestMatch(T, Tastes, TmpRes, TmpBest),
    matches(H, Tastes, Matches),
    (   Matches > TmpBest
    ->  Result = H,
        BestMatch = Matches
    ;   Result = TmpRes,
        BestMatch = TmpBest
    ).

Ok, let's test it:

?- getBestMatch([pizza], [italian, cheese], L, _).
L = pizza.

?- getBestMatch([pizza,lasagna,cake], [italian], L, _).
L = lasagna.

Seems to work at first glance, but the problem is that the order of dishes in the list gives a preference to the dishes (more on the right are strictly favored). This can be fixed as well, if wished.

For this altered predicate to work you need to change the call

getBestMatch(Breakfasts, Tastes, Result, -1).

to

getBestMatch(Breakfasts, Tastes, Result, _).

The underscore _ means this is a variable where you are not interested in its value.

Test for recommend/3 :

?- recommend(lunch, [warm, meat],L).
L = lasagna.

?- recommend(lunch, [warm, cheese],L).
L = pizza.

?- recommend(lunch, [sweet], L).
L = pizza.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM