首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Prolog中的知识表示--如何存储数据?

Prolog中的知识表示--如何存储数据?
EN

Stack Overflow用户
提问于 2014-09-30 10:59:02
回答 2查看 837关注 0票数 4

当我在Prolog中批评我的数据结构时,我在这里向专家寻求替代解决方案。

例如,我有一组XML中的食谱转换数据集。

代码语言:javascript
复制
<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中:

代码语言:javascript
复制
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',[],...
    )] 
)]).

我想做的是根据用户的输入轻松地查找菜谱。用户可以给出一个成分或菜谱名称的一部分作为输入。

实际上,我是通过

代码语言:javascript
复制
ask_element(Name, Child, Parent) :-
        (
            member( element(Name,_,Child),Parent)
        ;
            member( element(_,_,NewParent),Parent),
            [_|_] = NewParent,
            ask_element(Name, Child, NewParent)
        ).

我得到了所有含有特殊配料的菜谱

代码语言:javascript
复制
 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风格?

在保罗·莫拉的评论(谢谢)之后,有一个想法是把数据整理成

代码语言:javascript
复制
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数据存储。我会很感激每一个提示。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-09-30 16:16:12

如果您想从XML文件中表示的Prolog信息中访问,Carlo的解决方案是一个很好的解决方案。

但是,让我们假设您希望在Prolog中表示所有的菜谱。正如您所描述的,一种解决方案是每个菜谱使用一个事实,其结构最适合您的应用程序中最常见的数据访问模式。正如您还注意到的,寻找使用特定成分的菜谱或要求进行特定测试是没有效率的,因为您必须从配方事实到配料列表(或步骤),然后对该列表进行线性搜索(您可以使用二进制搜索树而不是列表,但我怀疑可能的低数量的项目将证明它在计算上是合理的)。此外,在您的问题中添加新的描述符(如level/1 )需要将更改传播到所有访问配方数据的代码。考虑到这些问题,可能值得为菜谱使用一个模块或对象表示。这样做的想法是,每个菜谱将由一个模块或对象表示,每个属性都有一个谓词。有了这种表示,访问成分的计算成本将与访问配方名称或其步骤之一的成本相同。例如,在搜索含有特定成分的菜谱时,枚举所有配方模块或对象的必要步骤是一种廉价的操作。添加新的描述符对于对象表示来说很容易,也可以使用模块表示(本质上,您只需修改菜谱接口,可能会为新描述符添加一个默认值)。也有可能有混合表示,在某些情况下,这样的解决方案是合理的。如果您共享更多关于要应用到菜谱数据库的访问或推理的详细信息,则会更容易提供建议。

更新:示例,基于面向Logtalk对象的Prolog扩展(您可以在大多数Prolog实现中使用它,包括GNU和SWI)。有几种变化是可能的。对于使用相反模块的接口/协议的概念,请参见此帖子

代码语言:javascript
复制
:- 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.

示例查询:

代码语言:javascript
复制
?- green_soup::ingredients(Ingredients).
Ingredients = [pea, cream].

?- conforms_to_protocol(Recipe, recipep), Recipe::ingredient(pea).
Recipe = green_soup ;
Recipe = mashed_peas ;
false.

现在假设稍后要向所有菜谱添加一个level/1描述符。只是为了好玩,让我们用热补:

代码语言:javascript
复制
:- category(add_recipe_level_descriptor, complements(proto_recipe)).

    :- public(level/1).
    :- dynamic(level/1).

:- end_category.

您现在可以添加您的烹饪经验。你做绿汤的时候总是惹上麻烦。

代码语言:javascript
复制
?- green_soup::assertz(level(hard)).
true.

但是大多数菜谱都很简单,所以让我们为所有菜谱添加一个默认值:

代码语言:javascript
复制
:- category(recipe_level_default_value, complements(proto_recipe)).

    level(easy).

:- end_category.

现在你可以问:

代码语言:javascript
复制
?- mashed_peas::level(Level).
Level = easy.

我省略了一些细节(例如设置和编译/加载步骤),但希望这能让您了解什么是可能的(但完整的运行示例这里)。

票数 1
EN

Stack Overflow用户

发布于 2014-09-30 11:53:05

SWI提供了库(xpath),允许引用节点和属性“Prolog样式”。在回溯时,实例化将返回给调用方。因此,您可以使用findall等,因为您认为最合适。

代码语言:javascript
复制
?- database(Db), xpath(Db, //recipe, Recipe).

会列举所有的食谱。图书馆很强大,但不容易学习。看看你看到的(微不足道的)例子..。

您也可以查看这里,我建议library(xpath)处理GCC XML。我用它来构建我的SWI / OpenGL接口..。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26119110

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档