Arguments are not sufficiently instantiated while trying to get combinations

72 Views Asked by At

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

1

There are 1 best solutions below

0
David Tonhofer On BEST ANSWER

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:

  • Compute the calorific sum in 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 constraint logic programming which doesn't need to "compute immediately" and internally constructs datastructures corresponding to partially-filled in expressions which can be evaluated "later" (and possibly failed) when more is known:
:- 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.