2014-09-30 78 views
4

因爲我在Prolog中得到了關於我的數據結構的批評,所以我在這裏向專家詢問了替代解決方案。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> 

我選擇將其存儲在序言作爲迭代元素樹使用列表:

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',[],... 
    )] 
)]). 

我想什麼做的是根據用戶輸入輕鬆查看食譜。 用戶可能會將配料或配方名稱的一部分作爲輸入。

其實我跑throught元素通過

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...)建設,我不必改變電話。 但是迴應會更好,只返回ID號,然後我在一個「呼叫」(recipe(123,X,Y,Z))中獲得整個配方以進一步處理。其實我 返回作爲響應「列表中的字符串文本」,你看到它在上面的「袋」...

這是我在Prolog的第一個應用程序,所以我不是很熟悉充足的數據存儲。我會感激每一個提示。

+0

在我的建議中,成分和步驟會有一個參數,分別是成分和步驟的列表。即使用你的例子,配方(ID號,'綠色湯',成分([項目(500,格蘭姆,'豌豆'),項目(200毫升,'奶油')]),步驟(['做點什麼'下一步再做一次']))。'。將配料和步驟作爲具有配方依賴性的複合術語將會使處理變得複雜,而沒有任何益處。這也將更貼近您呈現的XML結構(假設您的意圖是)。 – 2014-09-30 11:49:01

回答

1

如果您想從Prolog信息中表示XML文件,Carlo的解決方案是一個很好的解決方案。

但讓我們假設您想要在Prolog中表示所有食譜。正如您所描述的,一種解決方案是針對每個配方使用一個事實,其結構最適合應用程序中最常見的數據訪問模式。正如你也注意到的,尋找例如使用特定成分或需要特定測試的食譜效率不高,因爲您必須從食譜事實到成分(或步驟)列表,然後在該列表上進行線性搜索(您可以使用二分查找樹而不是列表但我懷疑可能的低數量項目會計算)。此外,在您的問題中添加新描述符(如level/1)需要進行更改,以便可能傳播到訪問配方數據的所有代碼。考慮到這個問題,可能需要考慮使用模塊或配方的對象表示。這個想法是,每個配方將由一個模塊或對象來表示,每個屬性有一個謂詞。通過這種表示,訪問配料的計算成本將與訪問配方名稱或其一個步驟的成本相同。當例如搜索具有特定成分的食譜,列舉所有食譜模塊或對象的必要步驟是一個便宜的操作。使用對象表示方式添加新的描述符很容易,也可以用模塊表示法進行攻擊(本質上,您只需修改配方接口,可能會爲新描述符添加默認值)。也可能有混合表示,並且有些情況下這種解決方案是合理的。如果您分享關於您應用於食譜數據庫的訪問或推理的更多詳細信息,建議更容易。

更新:一個基於Logolog面向對象的Prolog擴展(可用於大多數Prolog實現,包括GNU Prolog和SWI-Prolog)的示例。幾種變化是可能的。爲了使用替代模塊來竊聽接口/協議的概念,請參閱例如這post

:- 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. 

我已經省略了一些細節(例如設置和編譯/加載步驟),但希望這可以讓您瞭解什麼是可能的(但完整的運行示例here)。

+0

再次感謝您的輸入:)說實話模塊或對象表示在Prolog中是新的。也許這會超過我的問題。我不想以最快的速度訪問數據庫,而只是「不要傻傻的工作」。我的主題是一個對話組件,接近「語言理解過程」,以便用戶可以輸入「我有一個雞蛋和一杯奶油,我可以做什麼?」。因此,數據庫中的搜索僅限於關鍵字,作爲食譜的配料,食譜名稱或其他描述符的一部分。 [...] – kiw 2014-10-01 09:13:04

+0

[...]我不需要保留原始的XML結構,因爲我在啓動時加載數據時需要在Prolog中有一個表示。所以我猜想你的第一個建議是用食譜(ID號,'綠色湯',配料([item(500,gramm,'pea'),item(200,ml,'cream')]),steps(['Do something ','下一步再做一件事']))'基於列表作爲參數是足夠好的。 [...] – kiw 2014-10-01 09:13:27

+0

希望我的最後一個問題:如果我想擴展數據模型以將不同部分的成分列表分開,那麼擴展列表內容如下是一個好主意:'配方(ID號,'綠色湯',內容([部分('主菜',配料([項目(500,格蘭姆,'豌豆'),項目(200毫升,'奶油')]),部分',items([item(200,gramm,'bread')]))]),steps(['Do something','Next step do something']))'或者我把它複雜到很多?必須知道,我是第一個使用Prolog的人,所以我對這個平臺和您的輸入真的很高興!! – kiw 2014-10-01 09:13:34

2

SWI-Prolog提供圖書館(xpath),允許引用節點和屬性「序言風格」。 回溯時,實例返回給調用者。所以你可以在findall等中使用,因爲你認爲最合適。

?- database(Db), xpath(Db, //recipe, Recipe). 

將枚舉所有食譜。圖書館功能強大,但不容易學習。看看你在那裏看到的(微不足道的)例子...

你也可以看看here,我回答了建議library(xpath)來處理GCC XML。我用它來構建我的SWI/OpenGL接口...

+0

感謝您的提示,這將有所幫助,但我使用的是GnuProlog ... – kiw 2014-10-01 08:49:15

+0

,因爲您已經使用了SWI使用的相同的XML表示形式,可能值得將您的訪問謂詞包裝在兼容的界面中。如果你保持基本,應該相當簡單的做...實際上,它歸結爲已經由成員/ 2執行的「懶惰評估」和自定義模式匹配。不難...此外,它顯示了一個重要的體系結構點,即如何有效地處理**大樹,使用xpath_chk(映射到memberchk,我認爲) – CapelliC 2014-10-01 08:55:25

+0

...相似性是因爲我開始了我的第一步在SWI Prolog中。由於許可證的具體情況,這裏的決定是使用GNU Prolog ...在Windows上運行GNU Prolog(或gplc)的問題,也可能不是最後的決定...... – kiw 2014-10-01 11:22:28

相關問題