Reputation: 1771
I'm new to Prolog and quite confused. We're just moving in to recursion and members. I have the task to find different combinations of fast food given the type, calories, and price:
burger 450 $5
fries 250 $2
shake 500 $3
salad 150 $7
I created a rule:
findfood(Calories, Price, Burger, Fries, Shake, Salad) :-
member(Burger, [0,1]),
...,
C is Burger*450 + Fries*250 + ...,
Price is Burger*5 + Fries*2 + ...,
C =< Calories.
where '...' indicates a continuation of the previous to the end of the list of food items. Essentially, you can purchase 1 of each item and indicate such with a 0
or a 1
(this is all part of the specifications).
I need to find the most expensive food combination less than or equal to a certain caloric amount. I am allowed to create multiple rules to do this. It should be something like:
|?- most_expensive(1000, Price, Burger, Fries, Shake, Salad)
and it will return the most expensive combination of food for 1000 calories. I know I'll need to do some sort of recursive comparison, but I'm not sure how to proceed. For the record, I understand recursion pretty well, just not Prolog. Can anyone perhaps explain how I might accomplish this?
Upvotes: 0
Views: 169
Reputation: 22803
So, you elided some of the code, but I gather this is what you have:
findfood(Calories, Price, Burger, Fries, Shake, Salad) :-
member(Burger, [0,1]),
member(Fries, [0,1]),
member(Shake, [0,1]),
member(Salad, [0,1]),
Calories is Burger * 450 + Fries * 250 + Shake * 500 + Salad * 150,
Price is Burger * 5 + Fries * 2 + Shake * 3 + Salad * 7.
This seems sensible, if labor-intensive. Now you can easily enumerate all of the possibilities following @mbratch's advice:
all_combos(Combinations) :-
setof((C,P,B,F,Sh,Sa), findfood(C,P,B,F,Sh,Sa), Combinations).
You can also abuse Prolog to get the solution you want from this:
most_expensive_under(MaximumCalories, Calories, Price, Burgers, Fries, Shakes, Salads) :-
all_combos(Combos),
member((Calories, Price, Burgers, Fries, Shakes, Salads), Combos),
Calories =< MaximumCalories,
\+ (member((Calories2, Price2, _, _, _, _), Combos),
Calories2 =< MaximumCalories,
Price2 > Price).
This declares that the most expensive option is the one for which there is no other option satisfying the maximum calorie constraint with a greater price. Trying it seems to succeed:
?- most_expensive_under(1000, Calories, Price, Burgers, Fries, Shakes, Salads).
Calories = 850,
Price = 14,
Burgers = Fries, Fries = Salads, Salads = 1,
Shakes = 0 ;
false.
Bear in mind, this kind of solution is O(N^2). I bring it up really only to illustrate the power of unification—it's not a great idea, performance-wise. A better idea would be to use setof/3
, which sorts its results, as @mbratch suggests, or library(aggregate)
, which I've never used but hear good things about.
Upvotes: 1