当我在Prolog中批评我的数据结构时,我在这里向专家寻求替代解决方案。
例如,我有一组XML中的食谱转换数据集。
<recipeml fileversion="13.8.2014">
<recipe>
<head>
<title>Green Soup</title>
</head>
<ing-div type="titled">
<title>soup</title>
<ingredients>
<ing>
<amt><qty>500</qty><unit>gramm</unit></amt>
<item>pea</item>
</ing>
<ing>
<amt><qty>200</qty><unit>ml</unit></amt>
<item>cream</item>
</ing>
...
</ingredients>
</ing-div>
<directions>
<step>Do something, cooking ....</step>
<step>Next do again something...</step>
...
</directions>
</recipe>
<recipe>
...
</recipe>
...
</recipeml>我选择使用列表将它作为迭代元素树存储在Prolog中:
database([element('recipeml',[version=0.5],
[element('recipe',[],
[element('head',[],
[element('title',[],['Green Soup']
)]
),
element('ing-div',[type=titled],
[element('title',[],['soup']),
element('ingredients',[],
[element(ing,[],
[ element(amt,[],
[ element(qty,[],['500']), element(unit,[],[gramm]),]),
element(item,[],['pea'])
]),
element(ing,[],
[ element(amt,[],
[ element(qty,[],['200']), element(unit,[],[ml]),]),
element(item,[],['pea'])
])
]
)]
)]
),
element('recipe',[],...
)]
)]).我想做的是根据用户的输入轻松地查找菜谱。用户可以给出一个成分或菜谱名称的一部分作为输入。
实际上,我是通过
ask_element(Name, Child, Parent) :-
(
member( element(Name,_,Child),Parent)
;
member( element(_,_,NewParent),Parent),
[_|_] = NewParent,
ask_element(Name, Child, NewParent)
).我得到了所有含有特殊配料的菜谱
findall(RTitle,
(
ask_element('recipe',RKnot,Knot),
ask_element('item',TmpIng,RKnot),
contains(TmpIng,Ingredient),
[Ing|_] = TmpIng, % avoid brackets [Egg]
define_xml_knot(['head','title'],_,RKnot,TmpRTitle),
[RTitle|_] = TmpRTitle % avoid brackets [Soup]
,Bag),我的结果是一个食谱标题的列表。如果输入了一个配料列表,我需要第二个分析步骤来获得最匹配的配料的配方。也许这不是真正的Prolog风格?
在保罗·莫拉的评论(谢谢)之后,有一个想法是把数据整理成
recipe(IDnumber,'Green Soup',ingredients(item(500,gramm,'pea'),item(200,ml,'cream')),steps('Do something','Next step do again something')).我不知道这是否真的有帮助。寻找一种含有某种成分的配方,我必须在每一个菜谱中一步一步地看,如果我正在寻找的成分(或单词的一部分)包含在每个项目中。如果我想添加一个新的描述符,例如"level(easy)“,我必须随着recipe()中元素数量的变化而更改所有数据调用。使用element(element...)结构,我不必更改调用。但是响应会更好,只返回IDnumber,然后在一个“调用”(recipe(123,X,Y,Z))中得到整个菜谱以供进一步处理。实际上,当您在上面的“包”中看到它时,我会作为响应“列表中的字符串文本”返回。
这是我在Prolog中的第一个应用程序,所以我不太熟悉adquate数据存储。我会很感激每一个提示。
发布于 2014-09-30 16:16:12
如果您想从XML文件中表示的Prolog信息中访问,Carlo的解决方案是一个很好的解决方案。
但是,让我们假设您希望在Prolog中表示所有的菜谱。正如您所描述的,一种解决方案是每个菜谱使用一个事实,其结构最适合您的应用程序中最常见的数据访问模式。正如您还注意到的,寻找使用特定成分的菜谱或要求进行特定测试是没有效率的,因为您必须从配方事实到配料列表(或步骤),然后对该列表进行线性搜索(您可以使用二进制搜索树而不是列表,但我怀疑可能的低数量的项目将证明它在计算上是合理的)。此外,在您的问题中添加新的描述符(如level/1 )需要将更改传播到所有访问配方数据的代码。考虑到这些问题,可能值得为菜谱使用一个模块或对象表示。这样做的想法是,每个菜谱将由一个模块或对象表示,每个属性都有一个谓词。有了这种表示,访问成分的计算成本将与访问配方名称或其步骤之一的成本相同。例如,在搜索含有特定成分的菜谱时,枚举所有配方模块或对象的必要步骤是一种廉价的操作。添加新的描述符对于对象表示来说很容易,也可以使用模块表示(本质上,您只需修改菜谱接口,可能会为新描述符添加一个默认值)。也有可能有混合表示,在某些情况下,这样的解决方案是合理的。如果您共享更多关于要应用到菜谱数据库的访问或推理的详细信息,则会更容易提供建议。
更新:示例,基于面向Logtalk对象的Prolog扩展(您可以在大多数Prolog实现中使用它,包括GNU和SWI)。有几种变化是可能的。对于使用相反模块的接口/协议的概念,请参见此帖子。
:- protocol(recipep).
:- public([
name/1, ingredient/3, step/1 % descriptors
]).
:- end_protocol.
:- object(proto_recipe, implements(recipep)).
:- public([
ingredient/1, ingredients/1, steps/1 % utility predicates
]).
ingredient(Ingredient) :-
::ingredient(Ingredient,_,_).
ingredients(Ingredients) :-
findall(Ingredient, ::ingredient(Ingredient,_,_), Ingredients).
steps(Steps) :-
findall(Step, ::step(Step), Steps).
:- end_object.
:- object(green_soup, extends(proto_recipe)).
name('Green Soup').
ingredient(pea, 500, gr).
ingredient(cream, 200, ml).
step(...).
...
:- end_object.
:- object(mashed_peas, extends(proto_recipe)).
name('Mashed Peas').
ingredient(pea, 700, gr).
ingredient(salt, 20, gr).
...
:- end_object.示例查询:
?- green_soup::ingredients(Ingredients).
Ingredients = [pea, cream].
?- conforms_to_protocol(Recipe, recipep), Recipe::ingredient(pea).
Recipe = green_soup ;
Recipe = mashed_peas ;
false.现在假设稍后要向所有菜谱添加一个level/1描述符。只是为了好玩,让我们用热补:
:- category(add_recipe_level_descriptor, complements(proto_recipe)).
:- public(level/1).
:- dynamic(level/1).
:- end_category.您现在可以添加您的烹饪经验。你做绿汤的时候总是惹上麻烦。
?- green_soup::assertz(level(hard)).
true.但是大多数菜谱都很简单,所以让我们为所有菜谱添加一个默认值:
:- category(recipe_level_default_value, complements(proto_recipe)).
level(easy).
:- end_category.现在你可以问:
?- mashed_peas::level(Level).
Level = easy.我省略了一些细节(例如设置和编译/加载步骤),但希望这能让您了解什么是可能的(但完整的运行示例这里)。
发布于 2014-09-30 11:53:05
SWI提供了库(xpath),允许引用节点和属性“Prolog样式”。在回溯时,实例化将返回给调用方。因此,您可以使用findall等,因为您认为最合适。
?- database(Db), xpath(Db, //recipe, Recipe).会列举所有的食谱。图书馆很强大,但不容易学习。看看你看到的(微不足道的)例子..。
您也可以查看这里,我建议library(xpath)处理GCC XML。我用它来构建我的SWI / OpenGL接口..。
https://stackoverflow.com/questions/26119110
复制相似问题