0

I'm new to prolog and trying to solve my made up following prolog's problem.

I have some people

person(john, 36).
person(jane, 3).
person(amber, 32).
person(emmy, 2).
person(clement, 37).
person(patrick, 15).
person(emilie, 20).

I have a list of compounds representing food items witch can be healthy (good).

foods([food(frechfries, _), food(apple, good), food(burger,_),
    food(kiwi, good), food(banana, good), food(potato, good), food(orange, good),
    food(cereal, good), food(hotdog, _), food(steak, _), food(coca, _), food(water, good)]).

I would like to match each item to one person, to distribute one and only one food to each person. To do so, I have the following rule:

distribute(P, F, Results) :- person(P, _), findall(Item, memberchk(Item, F), Results).

Unfortunately, everyone receive the first item of the list

Output

If I try to delete the used item with this rule:

distribute(P, F, Results) :- person(P, _), findall(Item, memberchk(Item, F), Results), delete(F, Item, F).

I get the same result. Anyone see what I'm missing?

Wizz
  • 43
  • 6
  • Your `foods` item is a little unclear. Why is it one fact with a complex list? Why not individual facts, `item(..., ...)`? And what does the `_` mean when you have, say, `item(burger, _)`? Do you really mean "not good"? `_` is a variable and really means *anything*. You should also rename `item` (which says nothing about what it means) to something like, `food_health` or something like that. – lurker Jun 07 '18 at 12:32
  • What happens if there are fewer foods than people? Are you allowing a food to be paired with more than one person, or do you want the pairings to be "unique"? Need some clarification on the conditions of this problem. – lurker Jun 07 '18 at 13:04
  • I @lurker. Thanks for the following. the _ mean it's whatever kind of food. Meaning it can be good or not. The idea is that, for the next step of this exercice, anyone under 20 years old must have "good" food. If there isn't enough food, prolog should respond with a 'no'. I'll rename "item" to "food" to be more accurate in the description. And I've used a list and not individual fact to discriminate the distributed food. The pairing must be unique. – Wizz Jun 07 '18 at 13:57
  • What do you want to happen if there are fewer foods than people? – lurker Jun 07 '18 at 14:09
  • If there are fewer foods than people, prolog should tell me there is no solution. Also @lurker, if a pairing can be done using individual facts instead of a list, it's welcome. Althought I have more difficulties to see how. I'm new to prolog mindset of doing thinks :-). – Wizz Jun 07 '18 at 14:12

1 Answers1

0

Generically speaking, it sounds like you have two collections of facts and you just want to define a rule that succeeds for each unique matching pair.

The sets of facts are person and food. As I mentioned in the comment, defining the foods as a single fact with a list will be a bit awkward. I would define food in the same way you are defining person, that is, as individual facts.

person(john, 36).
person(jane, 3).
person(amber, 32).
person(emmy, 2).
person(clement, 37).
person(patrick, 15).
person(emilie, 20).

food(frechfries, _).
food(apple, good).
food(burger,_).
food(kiwi, good).
food(banana, good).
food(potato, good).
food(orange, good).
food(cereal, good).
food(hotdog, _).
food(steak, _).
food(coca, _).
food(water, good).

Now we can think about defining a match-up. You can think of it this way:

A successful match-up consists of a list of person-food pairs in which the each person and each food only appear once (are uniquely selected from the respective collections) and each person is represented.

Since we're trying to select each person and food uniquely, I'm thinking select/3 would be a good choice as a core mechanism to achieve the desired goal.

person_food_pairs(PeopleFoodPairings) :-
    findall(Person, person(Person, _), People),
    findall(Food, food(Food, _), Foods),
    length(People, NumberOfPeople),
    length(Foods, NumberOfFoods),
    NumberOfPeople =< NumberOfFoods,
    person_food_pairing(People, Foods, PeopleFoodPairings).

person_food_pairing([], _, []).
person_food_pairing(People, Foods, [Person-Food|RemainingPairs]) :-
    select(Person, People, RemainingPeople),
    select(Food, Foods, RemainingFoods),
    person_food_pairing(RemainingPeople, RemainingFoods, RemainingPairs).

There are going to be a lot of combinations, so lots of solutions! I didn't see any other conditions you had which would limit this. Also, you could leave out this set of lines, and the code will still yield the same results due to the definition of person_food_pairing/2:

    length(People, NumberOfPeople),
    length(Foods, NumberOfFoods),
    NumberOfPeople =< NumberOfFoods,

However, then the code would do a lot of unnecessary execution just to finally determine it cannot do the pairing. So these lines help determine that case early.


If, as noted in the comments, you have conditions that involve some of the person and food attributes, you'll need to carry those attributes along until you do the pairing.
person_food_pairs(PeopleFoodPairings) :-
    findall(Person-Age, person(Person, Age), People),  % Include age
    findall(Food-Health, food(Food, Health), Foods),   % Include health factor
    length(People, NumberOfPeople),
    length(Foods, NumberOfFoods),
    NumberOfPeople =< NumberOfFoods,
    person_food_pairing(People, Foods, PeopleFoodPairings).

person_food_pairing([], _, []).
person_food_pairing(People, Foods, [Person-Food|RemainingPairs]) :-
    select(Person-Age, People, RemainingPeople),  % Select person-age pair
    select(Food-Health, Foods, RemainingFoods),   % Select food-health pair
    (Age < 20 -> Health == good ; true),          % Condition to include
    person_food_pairing(RemainingPeople, RemainingFoods, RemainingPairs).

Note the use of ==/2 here and not unification =/2. The reason for this is that you want _ not to match good. If you use unification, then Prolog will successfully unify the variable _ with the atom good. ==/2, on the other hand, checks if these two terms are truly the same and does not do unification.

lurker
  • 56,987
  • 9
  • 69
  • 103
  • Thanks for the response. The first solution is accepted as it is. A criteria (that could limit a little bit the number of solution) to include is that anyone under 20 years old can only eat « good » food. I test your solution as soon as I can. – Wizz Jun 07 '18 at 16:13
  • @clem.rwan ok that sounds good. You can, as a good Prolog exercise, figure out how to add that condition to the code I provided. Let me know if you need hints. – lurker Jun 07 '18 at 16:20
  • @clem.rwan I decided to add the condition check to my answer. – lurker Jun 07 '18 at 16:35
  • You rock! I've tested both solutions and they work like a charm. I would never have found it by myself. On a scale from 1 to 10, how hard do you found this exercice? Being new to Prolog, I still have a long way to go to catch up to the awesome mindset needed to perform logic programming well enough. Thanks again. Just by curiosity, I'm going to look for some other criteria to filter the number of solutions. – Wizz Jun 07 '18 at 18:46
  • Hi @lurker. Thanks for all your answers. It did help me a lot . – Wizz Jun 08 '18 at 14:31