CLIPS 是一种计算机语言,又称专家系统。可以到 sourceforge.net 下载相关的开发工具,在 Mac 上安装后是这样的:
仔细一看,发现自己又精通了一门语言的 Hello world!
打印。图中分成两个部分,分别如下所示:
前半部分:
(defrule hello
=>
(printout t "Hello World!" crlf)
(readline)
)
这里是主要的是核心代码。
后半部分:
(run)
这里主要是执行 前半部分 代码的命令,所以执行 (run)
之后 Hello World! 就被打印了。
至此可以发现一个规律: 括号很多。是的,CLIPS 的语法块都是使用 ()
括起来的,但是 =>
看起来很特殊,接下来详细解说一下上面的每一行。
- 第 1 行: 有一个关键字
defrule
, 意思是定义规则的意思。我猜可能是define rule
的缩写。hello
就是所定义规则的名称。这一行的开头是(
与最后一行的)
匹配。 - 第 2 行:
=>
这个就有点厉害了,暂且理解成一个语法分隔符,会在下面介绍。 - 第 3 行:
Hello World!
就是在这里输出的了。 - 第 4 行: 等待终端输入、读取信息。
现在应该已经对 CLIPS 的语法有简单的认识了。
上面仅仅是一个简单例子,一个相对比较完整的语法应该是这样的:
(defrule hello
(say hi)
=>
(printout t "Hello World!" crlf)
(readline)
)
当输入这段代码之后,在执行 (run)
的时候,发现 Hello World!
并没有输出,这么淘气,到底是为啥呢?接下来分析一下具体的区别,就是多了一句 (say hi)
。
是的、第一次看到这么写,尽然没有报错,括号直接写 say hi
,这是什么东西。在 CLIPS 中,这个叫 facts(事实)。那这个 facts 有什么作用呢?
在解释之前,先来回顾一下现在已经提到的两个关键词:rule(规则) 与 facts(事实)。
- rule(规则) : 就是上面
defrule
中的所有代码。 - facts(事实): 就是最后提到的
(say hi)
。
在 CLIPS 中所要解决的问题,都使用一个特定的规则来表示,在此同时,一个规则免不了依托于某些事实,事实可以是 0 个或者是多个。从上面的试验我们能得出一个结论,如果这个规则没有 facts,那么执行 (run)
之后 Hello World!
有输出,一旦添加了 facts,那么将不会有输出。这是因为在执行 (run)
的时候,系统会在系统内存中检测这个 rule 中是否有事实,没有就直接 fire(触发)
这个规则,一旦有 facts,那么需要这些 facts 都被执行之后,才会fire(触发)
这个规则。
我勒个去,那么问题又来了:facts(say hi) 怎么来的啊?这个 facts 又是怎么被 fire(触发)
呢?
新的概念又出现了:assert,比如 assert(say hi)
,就表示在内存中定义了一个 facts,叫 (say hi)。当定义之后,执行 (run)
之后,实时(say hi)
就被触发了。如果你很懵,看下面的图:
插播一个小总结
对应一个规则的定义,其模板应该是这样的:
(defrule rule_name "optinal_comment"
(pattern_1) ; 由一些在“=>”之前的元素组成的规则左部分
(pattern_2)
...
(pattern_N)
=>
(action_1) ; 由一些在“=>”之后的元素组成的规则右部分
(action_2)
...
(action_M))
看了这个定义,聪明的你应该对 CLIPS 的理解更上一层楼。总之,通过上面对规则 hello
的介绍,加上这个定义,你应该知道在最后一个例子中:pattern
有一个,是(say hi),可以有多个。action
有两个,分别是 (printout..)
与 (readline)
,可以有多个。当然这些 action
被触发的条件是 action
按照 与(AND) 的关系成立之后。
用一个伪代码来解说 CLIPS 的规则就是:
IF certain conditions are true
THEN execute the following actions
上面伪代码中有两个关键词:IF 与 ** THEN。仔细品味,还真是这个道理。IF**代表 =>
的前半部分,称 LHS
, THEN 代表的是 =>
的后半部分,称 RHS
。
到这里、对 CLIPS 的介绍也该告一个段落了。但是学习不能停,在上面的例子中,希望将 hello 规则实现得更加通用一点,告诉别人是谁说的。由上面的介绍知道,可以写固定,但是这样肯定不可取的,毕竟写固定之后是不易于扩展的。接下来就需要介绍一下在 CLIPS 中的变量使用。我将上面的代码换成了这样子的:
(defrule hello
(say ?name)
=>
(printout t ?name" say: Hello World!" crlf)
(readline)
)
是的、仅仅是将 facts 换成了 (say ?name)
。没错,在 CLIPS 中定义变量就是这么单纯:一个问号之后直接加上一个可用字符串就是定义了,使用也是一样的。那如何去使用呢?文字很难描述,直接看图:
当创建一个 facts (say CoderHG)
之后就自动的匹配到 hello 规则中的 (say ?name)
了, 并直接将 CoderHG
赋值给了 ?name 变量。通用格式如下:
?
厉害了、我的锅,变量就这么介绍完了 ?!?!?!?!?!?!还有呢,别着急。关于变量、能否指定变量类型呢?可以的,比如有 INTEGER
与 STRING
, 其次还能自定义数据类型,请看如下定义:
; 使用模板定义一个数据类型(同结构体)
(deftemplate started-download
(slot time (type INTEGER))
(slot caller-address (type INTEGER))
(slot signpost-id (type INTEGER))
(slot image-name (type STRING))
)
在 CLIPS 中,这叫一个模块,定义一个模块的关键字是 deftemplate
。如同以上的定义一样,这个模板的名称叫 started-download,在这个模板中有这些属性:time
, caller-address
,signpost-id
与 image-name
。每个属性前面必须要有 slot, 后面括号后面的就是具体的数据类型。
是的,就是在 C、JAVA 中,这叫一个结构体。
再来插播其它的小 case
在上面我们主要是围绕一个 hello 规则来进行介绍的,那么另个问题来了。这么一个规则在 CLIPS 中放到什么地方了呢?答案是 内存中。这个答案略显 屌丝,但也无伤大雅。其实更确切的说这些规则都需要依托于在 CLIPS 的模块中。如果不明确的指明的话,默认是在 MAIN 模块中。我们可以这样的验证:
是的,我们可以使用函数 ppdefrule 来查看一个 rule 的定义,比如(ppdefrule hello)。可以看到在 hell 的前面有一个我们没有写的 MAIN::
,这是默认的,所以一般可以不用明显的写出来。
那么在 CLIPS 中海油其它的模块,比如:MODELER 与 RECORDER。
是该做一个总结了
在当前文章中主要介绍了这个内容:rule(规则)、facts(事实)、变量、模板与模块。
对于 CLIPS ,还有更多需要我们继续学习深究的东西,但是对于目前的我来说,知道这些差不多够用。
我对 CLIPS 的学习是针对在 iOS 开发中 《Xcode 中 Instruments 自定义》 中遇到的。Instruments 自定义 中的实现,都是居于 CLIPS 语言实现的。所以研究 Instruments 自定义 必须要知道 CLIPS 中的一些基本套路。最后附上两张 Instruments 自定义 的实现:
看到这里,就要说再见了。
如果有机会再详细的介绍 Xcode 中如何实现 自定义 Instruments 功能。
推荐文章:
从入门到精通之专家系统CLIPS(二)
Creating Custom Instruments