CLIPS中文手册 下载本文

活后,同权值或低权值之前,新的激活将会被放到议程中。这就是说议程中是从高权值到低权值进行排序的。

在本书中,所有的讨论和例子均是在假设为深度优先策略前提下的。

现在,你知道了所有的这些可选设置是多么的有用,一定得记住:当你运行一个由你和其他人共同编写的专家系统时,要保证你们的设置是一致的。否则,你会发现操作无效或者甚至是错误的。事实上,最好的办法是在你开发的过程中,对任何系统进行显式的设置编码,以保证正确配置。

自定义事实

当你使用CLIPS的时候,你也许会对在顶层中输入相同的声明事实而感到厌烦。如果你准备在程序运行的时候用到相同的声明,首先你可以用批处理文件加载磁盘里的声明,其次,你还可以使用自定义事实关键字:deffacts。举例如下:

CLIPS>(unwatch facts)

CLIPS>(unwatch activations) CLIPS>(clear)

CLIPS>(deffacts walk “Some facts about walking”

(status walking) ; 被声明的事实 (walk-sign walk)) ; 被声明的事实

CLIPS>(reset) ; 引入被自定义声明的事实 CLIPS>(facts)

f-0 (initial-fact) f-1 (status walking) f-2 (walk-sign walk) For a total of 3 facts. CLIPS>

自定义事实声明,必需指定一个事实名,如上面的walk,跟在关键字deffacts的后面,事实名后面可以跟由双引号包含的注释。同规则中的注释一样,当CLIPS载入(deffacts)事实时,(deffacts)的注释将会被保留。事实名或注释后面便是将要被声明到事实表中的事实,自定义的事实由CLIPS的(reset)命令声明添加。

事实(initial-fact)由(reset)命令自动添加进来,并且它的事实标识符一直是f-0。即使没有任何自定义的声明,(reset)命令也会自动声明事实(initial-fact)。在CLIPS的早期版本中,该事实被用来激活一些类型的规则,但是现在它早已不作此目的使用了。它被用来对那些显式匹配于该事实的程序向后兼容。

(reset)命令较之(clear)命令的一个好处是,它不会丢弃所有的规则。(reset)命令使规则完整无缺,而(clear)命令将会移除所有议程中的规则,并移除所有事实表中的旧的事实。用(reset)命令是开始一个程序执行的首选方法,特别是之前程序已经在运行并且事实表已经被旧的事实打乱时。

总而言之,(reset)命令作用于事实有三点:

(1)将存在的事实从事实表中移除,同时也会移除议程中的激活规则。 (2)声明事实(initial-fact)

(3)声明已自定义(deffacts)声明的事实。

事实上,(reset)命令对于对象也有相似的作用。它可以删除实例,创建initial-object,声明添加自定义实例(definstances)。

选择性消除

undeffacts命令的作用是通过消除内存中的自定义事实来撤销(deffacts)声明事实。举个例子:

CLIPS>(undeffacts walk) CLIPS>(reset)

CLIPS>(facts)

f-0 (initial-fact)

For a total of 1 fact. CLIPS>

这个例子演示了怎样将自定义的事实walk消除。如果执行了(undeffacts)后,想保存一个自定义事实声明,则必须重新定义。你甚至还可以使用(undeffacts)清除initial-fact事实。除了事实之外,CLIPS还允许使用undefrule命令消除选定的规则。

注意

你可以对议程监视规则(watch rules)触发和监视激活(watch activations)。监视统计(watch statistics)给出已经触发规则数,执行时间,每秒规则数,事实的平均数,事实的最大数,激活的平均数和激活的最大数等信息。这些统计信息对于调整专家系统、优化运行速度非常有用。另一个命令叫:“watch compilations”,用来显示当规则被加载时的信息。watch all命令监视所有的项目。

使用dribble命令打印和查看信息到屏幕或磁盘,将会使你的程序稍微变慢,这是因为CLIPS需要花较多的时间去打印或保存信息到磁盘中去。dribble-on命令会将所有的信息存储到被选入对话框的磁盘文件中,直到dribble-off命令的输入才终止。这在提供任何事情发生时的参数记录是非常方便的。这两个命令如下:

(dribble-on ) (dribble-off )

另外一个有用的调试命令是(run),该命令提供了一个触发规则数目的可选参数。举个例子,(run 21)命令将会告知CLIPS运行,并当21个规则触发后停止。(run 1)命令允许你每次只能执行一步程序。(step)命令等同于(run 1)。

像其它的编程语言一样,CLIPS也提供断点(breakpoints)支持,断点作为CLIPS的一个简单指示符,停止顺序执行而优先执行指定规则。断点由set-break命令设置。remove-break命令将移除已经设置的断点。show-breaks命令显示所有设置断点的规则。带参数(rulename)的规则句法如下所示:

(set-break ) (remove-break ) (show-breaks)

合适的匹配

你可能会遭遇到这种情况:当你确定某条规则应该被激活却没有被激活。这也许是你的CLIPS中存在有漏洞,因为对于一个技术非常好的CLIPS的程序员来说,应该不可能是他们的问题(注意:为开发者做些商业宣传)(这里是反语,幽默)。

多数情况下,出现错误的原因是你书写规则的方式不对。为了给调试提供帮助,CLIPS有一个被称为matches的命令,这个命令可以告诉你那些规则中的模式与事实可以匹配,哪些模式不能匹配而使规则不被激活。出现错误的一个普遍原因是,模式中的元素拼写错误导致与事实不匹配或增加的事实有拼写错误。

(matches)的参数为需要被检查匹配规则的规则名。让我们来看看(matches)起着怎样的作用,首先输入(clear)命令,然后输入下面的规则:

(defrule take-a-vacation

(work done) ; 条件因素1 (money plenty) ; 条件因素2 (reservations made) ; 条件因素3

=>

(printout t “Let’s go!!!” crlf))

下面将显示(matches)命令的用法,输入所示的命令,注意(watch facts)命令被开启,当你手动声明事实的时候,这是一个不错的方法,它可以提供给你一次检查事实拼写的机会。

CLIPS>(watch facts)

CLIPS>(assert (work done)) ==>f-1 (work done)

CLIPS>(matches take-a-vacation) Matches for Pattern 1 f-1

Matches for Pattern 2 None

Matches for Pattern 3 None

Partial matches for CEs 1 – 2 ; CE即条件元素 None

Partial matches for CEs 1 – 3 None

Activations None CLIPS>

通过(matches)命令,可以看到事实标识为f-1的事实与规则中的第一个模式或称之为条件因素可匹配。规则可能有N条模式,术语部分匹配(partial matches)是关于第N个模式与第一个事实匹配的所有设置,也就是说,部分匹配开始于规则的第一个模式,终止于任何一个模式,但不包含最后一个模式。当一个部分匹配不能成立时,CLIPS将不会继续检查后面的匹配。举个例子,一个规则有四个模式,有可能第一个和第二个模式或第三个模式都可能匹配成功,但,只有当所有的模式都匹配,这条规则才能被激活。

其他特性

这里有一些其他有用的关于自定义事的命令。举个例子,list-deffacts命令将会列出当前CLIPS载入的所有自定义事实的事实名。另一个有用的命令是ppdeffacts,它将所有存储的自定义事实信息打印出来。

函数 作用

assert-string 以字符串作为参数执行一个字符声明和作为一个无字符串事实的声明 str-cat 通过字符串(string concatenation)从单项目中构建一个单引号字符串 str-index 返回第一次出现子串的字符串索引(string index) sub-string 返回一个字符串的子字符串

str-compare 执行字符串比较(string compare) str-length 返回字符串的长度(string compare) sym-cat 返回连结符号

如果你想不用圆括号来输出多变量,最简单的方法就是用string implode function,implode$。

第四章 变量

没改变更甚于改变。

迄今为止,你已经了解了一些规则的类型,简单的阐述了规则的模式与事实匹配的一些内容。在本章中,你将会学到一些更有用的匹配和处理事实的方法。

认识变量

同其他编程语言一样,CLIPS也通过变量(variables)来存储值。与事实不同的是,事实是静态的且不会改变,而变量的内容是随着其分配的值的改变而动态(dynamic)变化的。相比之下,一旦一个事实被声明,它的字段仅仅只能被撤销和重新声明一个该字段的事实而修改,甚至,这些事实的撤销和声明修改(将在本章后面的deftemplate中详细描述)是通过你所知道的修改事实索引执行的。

变量名,或者称之为变量标识符(variable identifier),通常被写在一个问号的后面,即变量名。通用格式如下:

?

全局变量将在后面详细讲到,与上面的句法比较有些许不同。

如同其他的编程语言一样,变量名应该有一种好的命名方式,具有明确的含义。一些有效的变量名实例如下:

?x ?noun ?color

?sensor ?valve ?ducks-eaten

在一个变量能够被使用之前,它必须被分配一个值。下面是一个没有分配值的例子,尝试输入下面的代码,你将会看到CLIPS会响应一个错误消息:

CLIPS>(unwatch all) CLIPS>(clear)

CLIPS>(defrule test =>

(printout t ?x crlf))

[PRCCPDE3] Undefined variable x referenced in RHS of defrule.

ERROR:

(defrule MAIN::test

=>

(printout t ?x crlf)) CLIPS>

当CLIPS不能找到?x变量的约束值(value bound)时,便会抛出一个错误的提示。术语bound意味着对变量所分配的值。只有全局变量约束于所有的规则。其他所有的变量均约束于一条规则。在一条规则被触发前后,如果非全局变量没有被约束,那么当你尝试调用该变量时,CLIPS就会给出一个错误提示。

果断点

一个变量的惯用方式是:在LHS中匹配一个值,随后在RHS中对该变量进行约束。举例如下:

(defrule make-quack

(duck-sound ?sound) =>

(assert (sound-is ?sound)))