Learn Prolog Now 翻譯 - 第十一章 - 知識庫相關操做和解決方案的收集 - 第一節, 知識庫相關操做

Prolog中有四個知識庫相關的操做命令:assert,retract,asserta,assertz。讓咱們學習它們是如何使用的。假設從一個空白的知識庫開始,若是輸入命令:緩存

?- listing.

Prolog會簡單地回覆true,列表是空白的。app

假設咱們輸入這個命令:性能

?- assert(happy(mia)).

Prolog會回覆true(assert/1命令始終會成功)。可是重點不是這個命令可以成功,而是它對知識庫帶來的反作用。若是如今咱們輸入:學習

?- listing.
happy(mia).

即,知識庫已經再也不是空白的了:它如今包含了咱們聲明的一個事實。code

假設咱們繼續輸入四個assert命令:內存

?- assert(happy(vincent)).
true

?- assert(happy(marcellus)).
true

?- assert(happy(butch)).
true

?- assert(happy(vincent)).
true

若是咱們如今查詢知識庫的內容:數學

?- listing.

happy(mia).
happy(vincent).
happy(marcellus).
happy(butch).
happy(vincent).
true

咱們聲明的全部事實如今都存在在知識庫中了。注意happy(vincent)在知識庫中存在兩個,由於咱們聲明瞭兩次,看上去是合理的。變量

咱們使用的知識庫操做實際上已經更新了謂詞happy/1的含義。更通用地講,知識庫操做命令給予了咱們在運行程序時更新謂詞的能力。在運行期間更新謂詞定義稱爲動態謂詞,與之相對的是咱們以前定義和使用的靜態謂詞。大多數Prolog解釋器都堅持認爲應該顯式地聲明動態謂詞。咱們將會稍後介紹包含動態謂詞的例子,如今讓咱們繼續討論知識庫操做命令。搜索

到此爲止,咱們只經過聲明往知識庫中添加了事實,可是咱們也能夠添加規則。假如咱們想要聲明一個規則說若是任何人很高興,那麼他就很天真,即:語法

naive(X) :- happy(X).

咱們能夠這麼作:

assert((naive(X) :- happy(X))).

請注意這個命令的語法:咱們聲明的規則使用一對小括號括起來。若是咱們如今問知識庫有哪些內容:

happy(mia).
happy(vincent).
happy(marcellus).
happy(butch).
happy(vincent).

naive(A) :- happy(A).

如今咱們已經瞭解若是聲明新的信息到知識庫中,咱們應該也瞭解若是在不須要這些信息的時候,將它們從知識庫中移除。存在一個和assert/1相反的謂詞,名爲retract/1來達到這個目的。好比,若是咱們使用下面的命令:

?- retract(happy(marcellus)).

而後列出如今知識庫中的全部內容:

happy(mia).
happy(vincent).
happy(butch).
happy(vincent).

naive(A) :- happy(A).

能夠看到,happy(marcellus)這個事實已經被移除。

若是咱們繼續:

?- retract(happy(vincent)).

而後列出如今知識庫中的全部內容:

happy(mia).
happy(butch).
happy(vincent).

naive(A) :- happy(A).

請注意第一個happy(vincent),並且只有第一個這樣的事實被移除。

若是想要移除咱們定義的happy/1全部的相關信息,能夠使用變量:

?- retract(happy(X)).
X = mia;
X = butch;
X = vincent;
false

如今的知識庫中,只剩下一個規則:

?- listing.
naive(A) :- happy(A).



若是咱們但願對聲明的位置有更多的控制,這裏有兩個assert/1的變種,分別是:

  1. assertz。將聲明的內容放在知識庫的最後。
  2. asserta。將聲明的內容放在知識庫的開頭。

好比,假設咱們從一個空白知識庫開始,而後給出以下的命令:

?- assert( p(b)), assertz(p(c)), asserta(p(a)).

而後列出知識庫中全部的內容:

?- listing.

p(a).
p(b).
p(c).
true



知識庫操做是一項有用的技術。特別是用於保存計算結果時,因此在之後再問相同的問題,咱們就能夠不用再從新計算一次:咱們只須要在聲明的事實中直接查詢保存的結果便可。這種技術稱爲內存化,或者緩存,這種技術在一些應用中能夠顯著地提高性能。下面是如何使用這項技術的簡單示例:

:- dynamic lookup/3.

add_and_square(X, Y, Res) :- lookup(X, Y, Res), !.
add_and_square(X, Y, Res) :- Res is (X + Y) * (X + Y), assert(lookup(X, Y, Res)).

這個程序作了什麼?基本上講,它使用兩個數字X和Y,將它們相加,而後進行平方運算得出結果。好比,咱們查詢:

?- add_and_square(3, 7, X).
X = 100
true

可是重點在於:程序如何實現?首先,須要注意的是咱們已經聲明lookup/3爲一個動態謂詞。咱們須要在運行時可以修改lookup的定義。其次,請注意定義add_and_square/3時存在兩個子句。其中第二個子句是數學運算,而且將結果使用lookup/3謂詞保存到知識庫中(即,緩存了運算結果)。第一個子句檢查Prolog的當前知識庫,看是否存在已經運算過的結果,若是存在,就簡單地返回結果,並中斷第二個子句的執行。

下面是程序運行的例子。假設咱們進行另外一個查詢:

?- add_and_square(3, 4, Y).
Y = 49
true

若是咱們如今查詢知識庫中存在的信息會發現已經包括了:

lookup(3, 7 ,100).
lookup(3, 4, 49).

若是咱們再問Prolog關於3,4相加後平方的查詢,將不會再進行計算,而是直接返回已經計算過的結果。

有一個問題:咱們如何刪除全部咱們再也不須要的事實,若是咱們輸入命令:

?- retract(lookup(X, Y, Z)).

Prolog將會一個一個搜索全部的事實,而後詢問咱們是否想要刪除它們。可是存在一個更加簡便的方式,使用下面的命令:

?- retract(lookup(_, _, _)).

這個命令將會移除知識庫中全部lookup/3相關的事實。

關於知識庫操做的應用,還有一些建議:雖然這是一項有用的技術,可是知識庫操做可以致使一些不美觀,難以理解的代碼出現;若是你在一個存在不少回溯的程序中大量使用它們,理解程序含義會成爲噩夢。它是Prolog中一項沒有良好聲明性,非邏輯的技術,咱們須要很是當心地使用它。

相關文章
相關標籤/搜索