如何在Prolog中实现检索指定食谱全步骤食材的谓词
实现Prolog的retrieveIngredients谓词
嘿,我来帮你搞定这个Prolog谓词的实现!首先先把你现有的知识库贴出来,方便对照:
step('pancakes', 1, 'mix butter and sugar in a bowl', [butter, sugar], [bowl]). step('pancakes', 2, 'add eggs', [eggs], []). step('pancakes', 3, 'mix flour and baking powder', [flour, baking powder], []).
你需要实现的retrieveIngredients(X,Y),要能在查询retrieveIngredients('pancakes',Y)时返回所有用到的食材。这里有两种实用的实现方式:
方式一:用findall + flatten(简洁高效)
这种方式直接用Prolog内置的谓词来完成收集和整理,代码非常直观:
retrieveIngredients(Dish, Ingredients) :- % 收集所有对应菜品步骤里的食材列表 findall(StepIngredients, step(Dish, _, _, StepIngredients, _), AllIngredientsLists), % 把嵌套的列表展开成一维的食材列表 flatten(AllIngredientsLists, Ingredients).
逻辑拆解:
findall/3会遍历所有和目标菜品Dish相关的step事实,把每个步骤的食材列表(也就是每个step的第四个参数)收集起来,得到一个嵌套列表:[[butter,sugar], [eggs], [flour,baking powder]]。flatten/2把这个嵌套列表“压平”成一维列表,最终得到所有食材的集合:[butter, sugar, eggs, flour, baking powder]。
注:你给出的预期结果里没有sugar,应该是笔误哦,毕竟原知识库的第一步明确用到了sugar。如果确实需要过滤掉某些食材,可以在最后加额外的筛选逻辑~
方式二:递归遍历步骤(手动实现,更灵活)
如果你想手动控制遍历过程,不用内置的findall,可以用递归的方式逐个步骤收集食材:
% 对外暴露的主谓词 retrieveIngredients(Dish, Ingredients) :- retrieveIngredients(Dish, 1, [], Ingredients). % 递归子谓词:收集当前步骤的食材,继续下一个步骤 retrieveIngredients(Dish, StepNum, Accumulator, FinalIngredients) :- step(Dish, StepNum, _, StepIngredients, _), % 把当前步骤的食材加到累加器里 append(Accumulator, StepIngredients, NewAccumulator), % 计算下一个步骤号 NextStep is StepNum + 1, % 递归处理下一个步骤 retrieveIngredients(Dish, NextStep, NewAccumulator, FinalIngredients). % 基础情况:当前步骤号没有对应的步骤时,返回累加的食材 retrieveIngredients(_, StepNum, Accumulator, Accumulator) :- \+ step(_, StepNum, _, _, _).
这种方式从步骤1开始,一步步把每个步骤的食材加到累加器中,直到没有更多步骤为止,最终返回所有收集到的食材。
额外优化:去重与排序
如果你的菜谱里有重复使用的食材,想要去重的话,可以在第一种方式的最后加上sort/2(排序的同时自动去重):
retrieveIngredients(Dish, Ingredients) :- findall(StepIngredients, step(Dish, _, _, StepIngredients, _), AllIngredientsLists), flatten(AllIngredientsLists, Unfiltered), sort(Unfiltered, Ingredients).
不过sort会改变食材的顺序,如果想要保持步骤中的出现顺序且去重,就需要自定义一个去重谓词啦。
内容的提问来源于stack exchange,提问作者Coderman
相关产品推荐
相关产品推荐

