I'm learning Prolog and I want to build a recipe recommendation based on calories. So base on how many calories you put as input, I want to return the combinations of a breakfast, a lunch, and a dinner.
The breakfast, the dinner, and the lunch are composed differently, so in this case, the breakfast is composed of a drink and a dish, and the dish itself is composed of cereal, a fruit or a vegetable, and an animal origin food.
Right now, I'm trying to get all the possible breakfast I have the following Prolog code
dish([Cereal, FruitOrVegetable, AnimalOrigin], Calories) :-
cereal(Cereal, CerealCalories),
(
fruit(FruitOrVegetable, FruitOrVegetableCalories);
vegetable(FruitOrVegetable, FruitOrVegetableCalories)
),
animalOrigin(AnimalOrigin, AnimalOriginCalories),
Calories >= CerealCalories + FruitOrVegetableCalories + AnimalOriginCalories.
breakfast([Drink, Dish], Calories) :-
drink(Drink, DrinkCalories),
dish(Dish, DishCalories),
Calories >= DrinkCalories + DishCalories.
If I execute the dish function, just with 600 calories it works, it returns the combination of the given food, but when I tried to implement the same logic to compose the breakfast function it throws me the following error
Arguments are not sufficiently instantiated
And I search for a while, and I found that it because of a var that is inst initialized but I didn't found the origin of the problem.
An example of the declaration of the knowledge data base is
cereal(flakes, 386).
fruit(apple, 52).
vegetable(broccoli, 31).
animalOrigin(chicken_breast, 134).
drink(water, 0).
where the first is the name of the food, and the second the calories
The problem is that you call
dish(Dish, DishCalories)
from breakfast/2
, but DishCalories
is at that point an uninstantiated variable.
So when the Prolog Processor hits
Calories >= CerealCalories + FruitOrVegetableCalories + AnimalOriginCalories.
the variable Calories
, which is the same as the earlier DishCalories
, will be uninstantiated. There is nothing to compare!
You can either:
dish/2
and "pass it back out" so that a complete
sum of drink + disk can be computed breakfast/2
and then compare against Calories
as you would in other programming languages; or:- use_module(library(clpfd)).
dish([Cereal, FruitOrVegetable, AnimalOrigin], Calories) :-
cereal(Cereal, CerealCalories),
(
fruit(FruitOrVegetable, FruitOrVegetableCalories);
vegetable(FruitOrVegetable, FruitOrVegetableCalories)
),
animalOrigin(AnimalOrigin, AnimalOriginCalories),
%
% Compare if we know enough, otherwise delay the decision on >=
% and proceed optimistically
%
Calories #>=
CerealCalories +
FruitOrVegetableCalories +
AnimalOriginCalories.
breakfast([Drink, Dish], Calories) :-
drink(Drink, DrinkCalories),
dish(Dish, DishCalories),
format("Calories: ~q, DrinkCalories: ~q, DishCalories: ~q~n",
[Calories,DrinkCalories,DishCalories]),
Calories #>= DrinkCalories + DishCalories.
And so:
As usual:
?- dish(List,600).
List = [flakes, apple, chicken_breast] ;
List = [flakes, broccoli, chicken_breast].
But this works too, now:
?- breakfast(List,600).
Calories: 600, DrinkCalories: 0, DishCalories: _21370
List = [water, [flakes, apple, chicken_breast]] ;
Calories: 600, DrinkCalories: 0, DishCalories: _22506
List = [water, [flakes, broccoli, chicken_breast]].
If you don't even specify the Calories
:
Calories
is a number larger than 572:
?- breakfast(List,Calories).
Calories: _23482, DrinkCalories: 0, DishCalories: _23990
List = [water, [flakes, apple, chicken_breast]],
_25276 in 572..sup,
Calories#>=_25276,
Calories in 572..sup ;
or Calories
is a number larger than 551:
Calories: _23482, DrinkCalories: 0, DishCalories: _26724
List = [water, [flakes, broccoli, chicken_breast]],
_28010 in 551..sup,
Calories#>=_28010,
Calories in 551..sup.