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中一项没有良好声明性,非逻辑的技术,咱们须要很是当心地使用它。

相关文章
相关标签/搜索