CLIPS英文全稱為C LanguageIntegrated Production System,直譯的話就是“C語言整合產生式系統”,是由美國國家航天局約翰遜空間中心人工智慧部在1985年推出的專家系統開發工具,設計之初普遍應用於航空航天領域。CLIPS是一種支援多種編程風格的語言,包括:基於規則的、物件導向的和面向過程的編程風格。
1. 介紹
這是CLIPS 6.3基本編程指導手冊,主要面向於對CLIPS文法感興趣的使用者。學習本手冊出現一些程式設計語言的知識外,不需要任何專家系統的背景知識。本手冊第2節介紹CLIPS語言的整體架構和基本目標。第3節到11節詳細介紹CLIPS語言的主要內容,包括類型、規則、物件導向編程(COOL)等。第12節介紹action和function相關的內容,第13節介紹互動介面典型的命令。
本手冊僅介紹CLIPS編程的基本知識,對於使用者定義函數、嵌入式應用等內容,將在進階編程指南中介紹。使用者從本手冊可以實現簡單的專家系統。
2. CLIPS概述
2.1 CLIPS互動
CLIPS專家系統可以通過三種方式進行互動:1)簡單的命令列互動介面;2)提供的IDE互動介面(該方式在Interfaces手冊中介紹);3)嵌入到其他的系統被調用(該方式在進階編程指南中介紹)。此外,我們還可以將一系列命令寫到一個檔案中,在CLIPS啟動時讀取該檔案來自動執行相應的命令;或者可以使用batch命令來批量執行。
CLIPS最常用的互動方式是第一種方式。它的標準使用方法是:
1) 使用文字編輯器構建知識庫;
2) 載入知識庫到CLIPS;
3) 推理執行;
CLIPS提供介面以便於查看當前系統狀態、跟蹤執行路徑、增加或者移除資訊以及清空CLIPS。
2.1.1命令列方式互動
CLIPS使用的最主要互動方式即為命令列方式(頂層命令)。當進入CLIPS互動環境後,會出現“CLIPS>”提示符,輸入相應的命令後,該命令將會被解釋執行。命令可以是函數調用,結構定義、全域/局部變數定義、常量(包括符號常亮和數字)。如果輸入的是函數調用,該函數將被執行並返回對應的執行結果。函數調用使用首碼格式——即先寫函數名再跟運算元;結構定義則建立一種新的類型;輸入一個全域符號、局部符號或者常量,該符號或者常量的值將被顯示出來;局部變數能夠在命令互動介面中bind函數改變,它的值將保留到reset或者clear命令被調用。下面是一個簡單的例子。
2.1.2 命令自動執行和載入
在一些作業系統中,允許在程式啟動時自動載入一些額外的參數。當CLIPS運行在這樣的作業系統時,CLIPS將能夠在啟動時自動從檔案中運行相應的命令,載入一些結構定義。
在命令列中啟動CLIPS的文法如下所示:
clips <option>*
<option> ::= -f <filename> |-f2 <filename | -l <filename>
其中:
-f:同batch命令,如果-f的命令檔案中含有exit,則當檔案中的命令執行完後,自動結束clips;如果沒有exit命令,則命令執行完後仍然停留在clips環境中。
-f2:同batch*命令
-l:同load命令
2.1.3 與其他程式設計語言整合
當使用專家系統時,主要有兩種整合方式:將CLIPS嵌入式到其他系統;在CLIPS中調用外部函數;CLIPS兩種方式均可以支援,具體內容在進階編程指南中介紹。
2.2 引用手冊的文法
該手冊使用的術語描述方式與通用電腦引用手冊保持一致。文本字元即為字面含義;粗體詞或字元表示輸入內容的口頭描述;使用角括弧包含的連續字元表示使用者提供的類對象單個實體。非終結符後加“*”表示使用者提供的類對象的0個或多個實體;非終結符後加“+”表示使用者提供的類對象的1個或多個實體;兩個非終結符之間的省略符號也表示這個非終結符出現1次或多次。在中括弧內的項(如[<comment>])表示可選的;“|”表示多個項任選其一;空白僅用於分隔不同的項;“::=”表示非終結符能被怎樣替換。具體請見附錄G。
2.3 基本編程元素
CLIPS提供三種基本編程元素:未經處理資料類型,操作資料的函數,用於添加知識庫的結構。
2.3.1 資料類型
CLIPS提供8種未經處理資料類型:float,integer,symbol,string,external-address,fact-address,instance-name和instance-address。數字主要用float和integer表示,符號主要用symbols和strings表示。
數字僅僅由0~9/./e/+/-組成,CLIPS內部用long表示integer類型,用double表示float類型。同其他程式設計語言一樣,在比較兩個浮點數以及比較整數與浮點數時,要注意使用相對差值進行比較,不是直接使用比較運行符進行比較。
整型的樣本如下:
237 15 +12 -32
浮點的樣本如下:
237e3 15.09 +12.0 -32.3e-7
形式化表示如下:
symbol是字元敏感的,由可列印ASCII字元開始,後跟0到多個可列印的ASCII字元。symbol以非可列印字元(包括空格、Tab、斷行符號、換行)、雙引號,”(“,”)”, “&”,”|”,”<”,“~”,“;”作為符號結束標記。“’;”後跟的字元表示注釋,如果symbol以“<>”包含,則可以包括符號結束標記。此外,由於“?”和“$?”有特殊含義,符號也不能以這兩種類型作為開始字元。以下是字元的一些樣本:
foo Hello B76-HIbad_value 127A 456-93-039 @+=-% 2each
注意,其實數字也是symbol的一種特殊情況,只是其特殊性將其單獨劃為了float和integer兩種類型。
string是由雙引號括起來的任一字元,如果其內也保護雙引號,則使用\”表示,以下是字串的一席樣本:
“foo” “a and b” “你好” “a\”quote”
external-address表示被CLIPS調用的,由其他語言實現的API函數的傳回值資料結構。它只在調用外部函數時才會被產生,即不能定義一種這樣類型的資料。它的具體使用方式在進階編程手冊中介紹。在CLIPS內部,該類型的列印形式為:
<Pointer-XXXXX>
其中XXXXX表示外部地址。
fact是一些原子值構成的鏈表,通過fact索引值或者名字被引用。它在CLIPS內部的列印形式為:
<Fact-XXX>
其中XXX表示fact的索引值
instance是一個對象,表示一個執行個體或者是類的樣本。在CLIPS中,對象被定義為float,integer,symbol,string,multifield values,external-address,fact-address或者使用者定義類的instance。使用者定義類的instance通過make-instance建立,該執行個體可以通過引用地址唯一標識。所有這些將在2.4.2,2.5.2.3,2.6和第9節中詳細介紹。
instance-name是用中括弧擴起來的symbol(但外面的中括弧並不是instance-name的一部分),因此一般的符號不用中括弧括起來。但如果COOL(CLIPS Object Oriented Language)沒有包含在CLIPS的配置中,一般的符號也可以使用中括弧括起來。
instance-address僅在綁定函數調用傳回值到instance-address時或者綁定一個變數到一個匹配了規則左部對象的執行個體時才會產生。
引用一個使用者定義類的執行個體既可以使用instance-name,也可以使用instance-address,instance-address僅在強調速度時才使用。instance-address在CLIPS內部的列印形式為:
<Instance-XXX>
其中XXX為instance的名字。
在CLIPS中,包含一個value(可以是任意一種類型)的預留位置稱之為field。基礎資料型別 (Elementary Data Type)的value稱之為單域值(single-field value)。constant表示該值可以直接表示為一系列字串的不變單域值。多域值(multifield value)由0個或多個單域值組成,在顯示多域值時,CLIPS使用小括弧將其括起來。以下是多域值的一些樣本:
(a) (1 bar fool) () (x 3.0 “red” 567)
注意”(a)”與“a”是不同的。多域值產生主要有以下幾種情況:
1) 調用返回多域值的函數;
2) deffunction、objectmessage-handle、method中定義的萬用字元;
3) 在模式比對時綁定變數。
variable時一個儲存value的符號地址,它被用在CLIPS的構造類型(如defrule,deffunction,defmethod,defmessage-handle)中,其具體資訊將在相應的章節中介紹。
2.3.2 函數
function是CLIPS中一段可執行代碼,它使用一個函數名表示,返回有用的值或者產出有用的副作用(如顯示相關資訊)。本手冊裡所說的function通常表示返回有用值的函數。
function包括使用者定義函數和系統定義函數,他們都是用在外部環境中編寫再連結到CLIPS的環境中。系統定義函數已經整合到CLIPS環境中,可以直接調用;使用者定義函數是在CLIPS環境外定義的,需要使用extern加入到CLIPS環境中。詳細的系統定義函數列表見附錄H。
deffunction容許使用者在CLIPS環境中直接使用CLIPS的文法定義自己的函數。這類函數同使用者外部定義的函數相比,主要區別在於:使用deffunction定義的函數是被CLIPS解釋執行的,而使用者外部定義函數是在外部編譯完成,被CLIPS直接執行的。deffunction的詳細內容將在2.5.2.1節中介紹。此外還可以使用defgeneric和defmethod定義通用函數,這些函數實現多態,可以通過參數類型、個數的不同執行不同的代碼。通用函數將在2.5.2.2節詳細介紹。
CLIPS的函數調用使用首碼運算式表示。下面是一些乘法和加法的樣本。
2.3.3 結構
CLIPS中有大量定義結構:defmodule,defrule, deffacts, deftemplate,defglobal, deffunction, defclass, definstances,defmessage-handler, defgeneric和defmethod。結構與函數調用的區別在於:結構定義會影響CLIPS的環境,會往知識庫中添加或者刪除某些知識,但沒有任何輸出;函數調用通常不改變CLIPS的環境(除了clear、reset、open file類的函數調用),但會有傳回值或者輸出。
CLIPS的注釋使用“;”標註,“;”後面的該行所有內容均被看成是注釋。
2.4 資料抽象
CLIPS有三種形式表示資料抽象:facts,objects和globalvariables。
2.4.1 Facts
事實(Facts)是CLIPS系統中表示資訊的一種高層次形式。每個事實表示當前事實列表(Fact-list)中的一段資訊。事實是規則(rule)使用資料的基本單元。
事實列表通過assert(添加)、retract(刪除)、modify(修改)、duplicate(複製)等命令進行事實操作。事實列表的長度僅受限於主機的記憶體,如果一個事實已經在事實列表中,除非你修改事實列表的屬性(見13.4.4和13.4.5節),否則該事實將被忽略。
一些命令如(retract、modify、duplicate等)需要知道引用具體的事實,此時主要通過事實索引(從0開始)和事真實位址進行引用。事實索引在reset或clear命令後,將重新從0開始編號。事真實位址主要通過函數調用傳回值或者規則左部匹配時進行綁定。
事實標識(fact identifier)是一個簡單的顯示事實的方式。它的格式為“f-factIndex”。
事實的儲存分為有序和無序兩種方式。
2.4.1.1 有序方式
有序事實由一個符號域,後跟0個或多個域組成,域之間使用空格分隔,整個事實使用小括弧括起來。其中第一個符號可以標識後面域成員之間的關係。如可以使用“(father-of jack bill)”標識bill是jack的父親這樣一個事實。
無序事實第一個域可以是基本類型中的任意類型,且後面域沒有先後順序。但兩種類型的事實表示時均不能使用如下符號(這些保留字可以用於slot的名字,但不建議這麼使用),這些是CLIPS中的保留字,僅僅用在deftemplate中。
test, and, or,not, declare, logical, object, exists, and forall
2.4.1.2 無序方式
有序事實將資訊按照位置進行編碼。當需要訪問資訊時,使用者不但需要知道資料存放區在哪個事實中,而且還需要知道資訊存在在事實的哪個位置的域上。無序事實對每個域給出一個網域名稱稱,通過網域名稱稱對域中資訊進行訪問。無序事實通過deftemplate構件進行建立,該構件類似於結構定義。
無序事實與有序事實的區別在於第一個域的符號。所有事實第一個域都是符號,如果該符號對應deftemplate的名字後跟slot資訊,則該事實為無序事實,否則如果僅僅通過一個小括弧括起來,則為有序事實。下面是有序事實和無序事實的一些樣本。
a)無序事實
(client (name "Joe Brown") (idX9345A))
(point-mass (x-velocity 100) (y-velocity-200))
(class (teacher "Martha Jones")(#-students 30) (Room "37A"))
(grocery-list (#-of-items 3) (items breadmilk eggs))
無序事實不關心各個域出現的順序,下面表示的是同一個事實:
(class (teacher "Martha Jones")(#-students 30) (Room "37A"))
(class (#-students 30) (teacher"Martha Jones") (Room "37A"))
(class (Room "37A") (#-students30) (teacher "Martha Jones"))
b)有序事實
(class "Martha Jones" 30"37A")
(class 30 "Martha Jones""37A")
(class "37A" 30 "MarthaJones")
使用deftemplate定義的事實,除了可以添加和刪除外,還可以修改和複製。修改和複製的好處是僅僅改變某些需要變化的域,而不必改變不變的域。
2.4.1.3 初始化事實
deffacts可以初始化一批事實,它在每次reset時均會將相應的事實添加到事實列表。
2.4.2 Objects
在CLIPS中,對象(Object)可以被定為一個符號、一個字串、一個浮點數、一個整數、一個多域值、一個外部參考地址、一個使用者定義類的執行個體。對象包含兩個部分:屬性和行為。類(class)是定義對象行為和屬性的一個模板,而該類的執行個體即為對象。下面是一些對象及所屬類的例子。
在CLIPS中,對象分為兩種:原生類型對象和使用者定義類的執行個體。這兩種類型的對象在引用、建立、刪除以及屬性定義上都不相同。
原生類型的對象
使用者定義類的執行個體
引用方式
直接使用對應的值
根據名字或者地址引用
建立方式
CLIPS隱式建立
通過message或者專門的函數顯示建立
刪除方式
CLIPS隱式刪除
通過message或者專門的函數顯示刪除
屬性定義
CLIPS內部預定義
通過slot定義
使用方式
用於General function
用於OOP
對象slots和事實模板的主要區別在於繼承的概念。繼承使得對象的屬性和行為來自於父類的定義。COOL支援多繼承,一個類可以繼承自多個父類。由於繼承通常用於繼承slot和message-handle,而基本類型(如float、integer等),沒有slot和message-handle,因此通常不會從基本類型繼承。
2.4.2.1 初始化對象
通過definstances初始化一批對象,在調用reset時會自動將對象執行個體加入到執行個體鏈表(instance-list)中。
2.4.3 全域變數
全域變數定義後,容許在CLIPS整個環境中使用,同C語言中的全域變數,但CLIPS中的全域變數是弱類型的,即它並不限定為某個固定類型。與此不同,在一個構件中(如defrule、deffunction),可以定義局部變數,該變數僅能在定義的結構內使用。
2.5 知識表示
CLIPS提供啟發學習法範式和過程式範式表示知識。本節將介紹這兩種知識表示。物件導向知識表示將在下一節介紹。
2.5.1 啟發學習法知識表示——規則(rules)
規則(rule)是CLIPS中表示知識的一種主要方式,它用來表示啟發學習法的知識,即在某種條件成立的情況下執行一系列的操作。專家系統開發人員通過定義一系列規則以聯合起來解決一個問題。一條規則由前部和後部兩部分組成。規則的前部也叫做左邊(LHS),表示“如果”的部分,是規則被啟用的必要條件;規則的後部也稱為右邊(RHS),表示“則有”的部分,表示規則成立時執行的操作。
規則左部的每個事實或者執行個體成立與否主要查看它是否在事實列表(fact-list)上或者在執行個體列表(instance-list)上,它通過模式比對,利用推理機自動完成事實和執行個體匹配與否的檢測。
規則右邊表示的一系列操作在規則滿足時執行,當有多個規則同時滿足時,推理機使用衝突解決方案策略選擇其中的某條規則進行執行。當這條規則執行完畢後,推理機再選擇下一條規則進行執行,直到沒有待執行的規則為止。
在很多情況下,規則被認為是過程式編程(如C語言)的IF-THEN結構,只是在過程式編程時,IF-THEN結構僅在控制流程執行到該語句時才執行,而CLIPS的規則是一直會檢測,所以CLIPS的規則更像是WHENEVER-THEN。推理機總是保持了那些條件成立的規則路徑,因此規則在他們滿足時能夠立即執行,從這一點上說,規則又有點像Ada中的異常處理。
2.5.2 過程性知識表示
CLIPS也像C、Ada語言一樣支援過程性範式來表示知識。deffunctions和通用函數定義允許使用者定義新的執行單元以產生有用的副作用和傳回值。使用者可以像調用CLIPS的內建函數一樣調用這些新的函數。message-handlers容許使用者通過定義對訊息的響應來定義對象的行為。defunctions、generic functions和message-handlers都是使用者已定義流程化程式碼片段,它們被CLIPS在合適的時間內解釋執行。defmodules將不同的知識庫分隔開來。
2.5.2.1 Deffunctions
使用者通過deffunction在CLIPS環境中直接定義新的函數。在以前的CLIPS版本中,僅容許使用者通過在CLIPS外使用其他語言(如C、Ada等)定義函數,然後再通過外部函數調用的方式連結到CLIPS環境中。deffunction的函數定義了一系列表示式(如規則中的RHS),當該函數被調用時,由CLIPS依次按序執行。函數的返回結果是最後一個表示式的值。調用deffunction函數就像調用CLIPS中其他函數一樣。其具體內容將在第7章中介紹。
2.5.2.2 Generic Functions
Generic Functions同deffunction類似,也能在CLIPS中直接定義並被其他採用相同的方式調用。與deffunction不同的是,它能支援重載。你可以通過類型、參數個數的不同定義同名不同義的函數。具體內容將在第8章中介紹。
2.5.2.3 對象資訊傳遞(Object Message-Passing)
對象包括兩個方面:屬性和行為。對象屬性即對象類的slots,slots即為2.4.2中介紹的內容。對象行為是根據該對象連結的message-handlers操作定義的。對象通過訊息傳遞進行操作。如要使得Rolls-Royce對象(它是CAR類的一個執行個體)發動引擎,使用者可以調用“send”函數向Rolls-Royce發送“start-engine”的訊息。Rolls-Royce對象收到該訊息後,將該訊息傳遞給其所在類(即CAR類)及其父類的“start-engine”訊息的處理函數message-handler,從而完成相應的功能。message的處理結果同函數調用,它將產生有效副作用或者返回一個有用的值。更深層次的內容將在2.6和第9章中介紹。
2.5.2.4 defmodules
defmodules能夠將不同的知識庫區分開來。每一個構件都必須從屬於一個模組(module)。程式員能夠顯示的控制模組中的哪個構件對其他模組可見,模組可以見到其他模組中的哪些構件。不同模組間事實和執行個體的可見度也是相似的控制。模組也可以控制規則的執行流。該部分具體內容將在第10章中介紹。
2.6 CLIPS物件導向語言
這節將簡要介紹CLIPS中物件導向語言(COOL)。COOL包括資料抽象和知識表示。這節將給出COOL的整體介紹,主要包括COOL中兩個方面概念的接受。使用者定義類的執行個體請參考2.3.1節,對象結構請參考2.4.2和2.5.2.3節。COOL的詳細內容將在第9章中介紹。
2.6.1 COOL與純的物件導向編程範式的不同
在純物件導向編程中,程式中所有元素都是對象,對象僅能通過訊息進行操作。而在CLIPS中,對象僅限制於以下內容:floating-point and integer numbers, symbols, strings,multifield values, external-addresses, fact-addresses and instances of user-defined classes。所有的對象都可以通過訊息進行操作,而instances of user-defined classes必須通過訊息才能操作。如在純OOP系統中要將兩個數加起來,你需要先發送“add”資訊給第一個數對象,並將第二數對象作為參數傳遞給第一個數對象。在CLIPS中,你可以調用“+”函數,將兩個數作為函數的兩個參數傳遞進去,也可以像純OOP系統一樣,在NUMBER類上定義訊息處理函數。
所有不是對象的程式元素,你智能使用非OOP的函數來操作。如列印一條規則,你只能調用ppdefrule函數,而不能發送“print”給rule,因為rule不是對象。
2.6.2 物件導向編程的主要特徵
物件導向主要包括以下5個特徵:抽象(abstraction)、封裝(encapsulation)、繼承(inheritance)、多態(polymorphism)、動態綁定(dynamic binding)。抽象是高層的、直觀的表示複雜的概念。封裝是將處理過程實現細節掩藏起來,對外提供良定義的介面。繼承是指一個類可以從其他類的定義中繼承屬性和行為。多態是指不同對象採用各自獨特的方式處理相同的message。動態綁定是指只有在運行時才能推斷使用哪個message-handle來處理message。
COOL定義的新類抽象為新的資料類型。類型中的slots和message-handlers表示了該類對象的屬性和行為。
COOL支援封裝,使用者定義類的執行個體操作必須通過資訊傳遞。沒有定義相應message-handler方法的執行個體不能處理對應訊息。
COOL支援多繼承,其定義的類可以繼承自多個父類。所有類均有一個繼承鏈,如果鏈上有相同定義的屬性和行為,則使用離該類最近的父類屬性及行為。
COOL支援多態,它通過在不同的對象上綁定不同message-handler(但使用相同的名字)實現。
COOL的動態綁定表現在:在send函數調用的對象影響直到運行時才綁定。例如,一個執行個體名或變數在message發送時指向一個對象,但在另一個時刻可能指向另一個對象。
2.6.3 執行個體集查詢和分布是動作
為增強規則應用於對象的模式比對,COOL提供了一個有用的查詢系統,以決策、組合和執行使用者執行個體定義的一組操作,滿足使用者定義的標準。查詢系統容許你關聯到任意執行個體。你可以使用查詢系統決定:一個特殊的關聯集是否存在,你能儲存該集合以便於後續引用,或者你可以在該集合上依次迭代執行一個操作。其中一個應用的情境可能是:找出集合中所有年齡相同的男孩女孩對。