going over entire the list and check whether items exists in Prolog

Viewed 70

The database in my Prolog file looks like this ->

stage('crepe',2,'Stir the items into a consistent dough.',[],['pot','blender']).

I want to use the following query and get this result ->

?- equipmentStage(‘crepe’, ‘blender’, Y).
Y = [‘Stir the items into a consistent dough.’] .

I tried using the following code, but when I try the query it gives back an empty list. So Prolog concludes no such thing exists. Whereas when I try the query with the first element of the list, 'pot', in the database it gives back the correct output. ->

equipmentStage(Food,Equipment,StageTxt):-
findall(Txt, stage(Food, _, Txt, _, [Equipment]),StageTxt).

So my question is how do I also get the correct result with the second item in the list? I'm new to Prolog and the help would be really appreciated!

3 Answers

Here are my two main rules for using findall (and similar predicates like setof or bagof):

  1. Don't use findall. You probably don't need a list of results. Backtracking over results is usually what you want instead. Define a predicate that enumerates your solutions by backtracking.
  2. If you really really want a list (you probably don't), proceed as follows: Take your predicate definition from step 1. Apply findall to that; never put a complex query into findall.

So for your problem, step 1:

food_equipment_stage(Food, Equipment, Stage) :-
    stage(Food, _, Stage, _, Equipments),
    member(Equipment, Equipments).

This gives a single answer:

?- food_equipment_stage(crepe, blender, Stage).
Stage = 'Stir the items into a consistent dough.'.

With a bigger knowledge base, this would enumerate answers one by one.

Are you absolutely sure that you need a list, which you very probably don't? If yes (but probably no), in step 2 it is easy to apply findall to the definition from step 1 (but you probably don't even want to):

food_equipment_stages(Food, Equipment, Stages) :-
    findall(Stage, food_equipment_stage(Food, Equipment, Stage), Stages).

This gives you a list:

?- food_equipment_stages(crepe, blender, Stages).
Stages = ['Stir the items into a consistent dough.'].

Your findall matches only when there is one Equipment. You should just check if your equipment is in the list of equipments(Es) of recipe.

equipmentStage(Food,Equipment,StageTxt):-
    findall(Txt, (stage(Food, _, Txt, _, Es), member(Equipment, Es)),StageTxt).

If you want equipment listed with description :

equipment_desc(Food,[Equipment, StageTxt]):-
    stage(Food, _, StageTxt, _, Es),
    member(Equipment, Es).
?- equipmentStage('crepe', Ls).
Ls = [pot, 'Stir the items into a consistent dough.'] ;
Ls = [blender, 'Stir the items into a consistent dough.'].

If you want them as list instead of multiple solutions then you can just grab all the equipment and create the list using maplist as follows :

pair_rev(H, T, [T, H]).
equipment_desc_list(Food, Ls):-
    stage(Food, _, StageTxt, _, Es),
    maplist(pair_rev(StageTxt), Es, Ls).

Use member, the thing is findall checks and compares with the values it has. In your fact you have ['pot','blender'], if you try to give only 'blender', findall will reject it and you will be returned with an empty list []. So what you can do is use member to compare your 'blender', that checks it if it exists in ['pot','blender'] then it can easily execute.

stage('crepe',2,'Stir the items into a consistent dough.',[],['pot','blender']).

t(A,B,S):-
    stage(_,_,_,_,W),
    member(B,W),
    findall(C,stage(A,_,C,_,W),S).

?-t('crepe','blender',S).
S = ['Stir the items into a consistent dough.']
Related