?數(shù)據(jù)與表達式
1、領域
(1)標準領域.
Turbo prolog中不定義變量的類型,只說明謂詞中各個項的取值域.由上面我們知道, Turbo prolog 有整數(shù)、實數(shù)、字符、串 和 符號這5種標準域.另外,它還有結構、表和文件這3種復合域.
(2)結構.
結構也稱復合對象,它是 Turbo prolog 謂詞中的一種特殊的參量項(類似于謂詞邏輯中的函數(shù)).結構的一般形式為:
《函子》(《參量表》)1
其中函子及參量的標識符與謂詞相同.注意,這意味著結構中還可包含結構.所以,復合對象可表達樹形數(shù)據(jù)結構.例如下面的謂詞:
likes(“Tom”, sports(football, basketball, table_tennis)).1
中的
sports(football, basketball, table_tennis)1
就是一個結構,即復合對象.又如:
person(“張華”, student(“清華大學”), address(“中國”, “北京”)).
reading(“王宏”, book(“人工智能技術導論”, “西安電子科技大學出版社”)).
friend(father(“Li”), father(“Zhao”)).123
這幾個謂詞中都有復合對象.
復合對象在程序中的說明,需分層進行.例如,對于上面的謂詞:
likes(“Tom”, sports(football, basketball, table_tennis)).1
在程序中可說明如下:
domains
name = symbol
sy = symbol
sp = sports(sy, sy, sy)
predicates
likes(name, sp)123456
(3)表.
表的一般形式是:
[x1, x2, ..., xn]1
其中xi(i=1,2,...,n)為 Prolog 的項,一般要求同一個表的元素必須屬于同一領域.不含任何元素的表稱為空表,記為[].例如下面就是一些合法的表.
[1,2,3]
[ apple, orange, banana, grape, cane]
[“Prolog”, “MAENS”, “PROGRAMMING”, “in logic”]
[[a, b], [c, d], [e]]
[]12345
表的最大特點是其元素個數(shù)可在程序運行期間動態(tài)變化.表的元素也可以是結構或表,且這時其元素可以屬于不同領域.例如:
[name(“LiMing”), age(20), sex(male), address(xian)]
[[1, 2], [3, 4, 5], [6, 7]]12
都是合法的表.后一個例子說明,表也可以嵌套.
實際上,表是一種特殊的結構,它是遞歸結構的另一種表達形式.這個結構的函數(shù)名取決于具體的 Prolog 版本,這里我們就用一個圓點來表示.下面就是一些這樣的結構及它們的表的表示形式:
結構形式表形式
·(a, [])[a]
·(a, ·(b, []))[a, b]
·(a, ·(b, ·(c, [])))[a, b, c]
表的說明方法是在其組成元素的說明符后加一個星號*.如:
domains
lists = string*
predicates
pl(lists)1234
就說明謂詞 pl 中的項 lists 是一個由串 string 組成的表.
對于由結構組成的表,至少分3步說明.例如對于下面謂 p 中的表
對于由結構組成的表,至少分3步說明.例如對于下面謂 p 中的表
p([name(“Liming”), age(20)])1
則需這樣說明:
domains
rec=seg*
seg=name(string); age(integer)
predicates
p(rec)12345
2、常量與變量
由上面的領域可知, Turbo Prolog的常量有整數(shù)、實數(shù)、字符、串、符號、結構、表 和 文件 這8種數(shù)據(jù)類型.同理, Turbo Prolog 的變量也就有這8種取值.另外,變量名要求必須是以大寫字母或下劃線開頭的字母、數(shù)字和下劃線 序列,或者只有一個下劃線(這種變量稱為無名變量).
3、算術表達式
Turbo Prolog 提供了 5 種最基本的算術運算:加、減、乘、除 和 取模,相應運算符號為“+”、“-”、“*”、“/”、“mod”.這 5 種運算的順序為:“*”、“/”、“mod”優(yōu)先于“+”、“-”.同級從左到右按順序運算,括號優(yōu)先.
算術表達式的形式與數(shù)學中的形式基本一樣.例如:
數(shù)學中的算術表達式Turbo Prolog 中的算術表達式
x + yzX + Y * Z
ab - c / dA * B - C / D
u mod vU mod V(表示求U除以V所得的余數(shù))
即, Turbo Prolog 中算術表達式采用通常數(shù)學中使用的中綴形式.這種算術表達式為 Prolog 的一種異體結構,若以 Prolog 的結構形式來表示,則它們應為:
+(X, *(Y, Z))
-(*(A, B), /(C, D))
mod(U, V)123
所以,運算符“+”、“-”、“*”、“/”和“mod”實際也就是 Prolog 內部定義好了的函數(shù)符.
在 Turbo Prolog 程序中,如果一個算術表達式中的變元全部被實例化(即被約束),則這個算術表達式的值就會被求出.求出的值可用來實例化某變量,也可用來同其他數(shù)量進行比較,用一個算術表達式的值實例化一個變量的方法是用謂詞“is”或“=”來實現(xiàn)的.例如:
Y is X + 5或Y = X + 5 (*)123
就使變量 Y 實例化為 X+5 的值(當然 X 也必須經(jīng)已被某值實例化),可以看出,這里對變量 Y 的實例化方法類似于其他高級程序語言中的“賦值”,但又不同于賦值.例如,在 Prolog 中下面的式子是錯誤的:
X = X + 11
需要說明的是,雖然 Prolog 是一種邏輯程序設計語言,但在目前的硬件條件下卻非突破邏輯框架不可.這是因為有些實用操作是無法用邏輯描述的(如輸入與輸出),有些算術運算在原則上可用邏輯描述,但這樣做效率太低.為此, Prolog 提供了若干內部謂詞(亦稱 預定義謂詞),來實現(xiàn)算術運算、輸入與輸出等操作.所謂內部謂詞,就是 Prolog 的解釋程序中,預先用實現(xiàn)語言定義好的用戶可直接作為子目標調用的謂詞.一般的 Prolog 實用系統(tǒng)都配有 100 個以上的內部謂詞,這些內部謂詞涉及輸入輸出、算術運算、搜索控制、文件操作和圖形聲音等方面,它們是實用 Prolog 程序設計所必不可少的.這樣,上面的(*)式以及下面的關系表達式稱為異體謂詞.
4、關系表達式
Turbo Prolog 提供了 6 種常用的關系運算,即 小于、小于或等于、等于、大于、大于或等于、不等于,其運算符依次為:
《, 《=, =,》,》=, 《》1
Turbo Prolog 的關系表達式的形式和數(shù)學中的也基本一樣,例如:
數(shù)學中的關系式Turbo Prolog 中的關系式
X + 1 ≥ YX + 1》= Y
X ≠ YX 《》 Y
即, Turbo Prolog 中的關系式也用中綴形式.當然,這種關系式為 Turbo Prolog 中的異體原子.若按 Turbo Prolog 中的原子形式來表示,則上面的兩個例子為:
》=(X +1, Y) 和 《》(X, Y)1
所以上述 6 種關系運算符,實際上也就是 Turbo Prolog 內部定義好了的 6 個謂詞.這 6 個關系運算符可用來比較兩個算術表達式的大小.例如:
brother(Name1, Name2) :- person(Name1, man, Age1),
person(Name2, man, Age2),
mother(Z, Name1), mother(Z, Name2), Age1》 Age2.123
需要說明的是,“=”的用法比較特殊,它既可以表示比較,也可以表示約束值,即使在同一個規(guī)則中的同一個“=”也是如此.例如:
p(X, Y, Z) :- Z = X + Y.1
當變量 X、Y、Z全部被實例化時,“=”就是比較符.如對于問題:
Goal: p(3, 5, 8).1
機器回答“yes”,而對于:
Goal: p(3, 5, 7).1
機器回答“no”.即這時機器把 X+Y 的值與Z的值進行比較.但當 X,Y 被實例化,而 Z 未被實例化時, “=”號就是約束符,如:
Goal: P(3, 5, Z).1
機器回答“Z = 8”.這時,機器使 Z 實例化為 X+Y 的結果.
?輸入與輸出
雖然 Prolog 能自動輸出目標子句中的變量的值,但這種輸出功能必定有限,往往不能滿足實際需要;另外,對通常大多數(shù)的程序來說,運行時從鍵盤上輸人有關數(shù)據(jù)或信息也是必不可少的.為此每種具體 Prolog 一般都提供專門的輸人和輸出謂詞,供用戶直接調用.例如,下面就是 Turbo Prolog 的幾種輸入輸出謂詞:
readin(X).這個謂詞的功能是從鍵盤上讀取一個字符串,然后約束給變量 X .
readint(X).這個謂詞的功能是從鍵盤上讀取一個整數(shù),然后約束給變量 X,如果鍵盤上打入的不是整數(shù),則該謂詞失敗.
readreal(X).這個謂詞的功能是從鍵盤上讀取一個實數(shù),然后約束給變量 X,如果鍵盤上打入的不是實數(shù),則該謂詞失敗.
readchar(X).這個謂詞的功能是從鍵盤上讀取一個字符,然后約束給變量 X,如果鍵盤上打入的不是單個字符,則該謂詞失敗.
write(X1, X2, …, Xn).這個謂詞的功能是把項 Xi(i=1,2,…,n) 的值顯示在屏幕上或者打印在紙上,當有某個 Xi 未實例化時,該謂詞失敗.其中的 Xi 可以是變量,也可以是字符串或數(shù)字.例如:
write(“computer”, “Prolog”, Y, 1992)
nl(換行謂詞).它使后面的輸出(如果有的話)另起一行.另外,利用 write的輸出項“ ”也同樣可起到換行作用.例如:
write(“name”), nl, write(“age”)
與
write(“name”, “ ”, “age”)
的效果完全一樣.
舉個例子:
用上面的輸入輸出謂詞編寫一個簡單的學生成績數(shù)據(jù)庫查詢程序.
PREDICATES
student(integer, string, real)
grade
GOAL
grade.
CLAUSES
student(1, “張三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
grade :- write(“請輸人姓名:”), readln(Name),
student(_, Name, Score),
nl, write(Name, “的成績是”, Score).
grade :- write(“對不起,找不到這個學生!”).12345678910111213
下面是程序運行時的屏幕顯示
請輸入姓名:王五
王五的成績是96.412
四、分支與循環(huán)
Prolog 本來沒有分支和循環(huán)語句,但可以利用其邏輯機制實現(xiàn)分支和循環(huán)效果.
1、分支
對于通常的 IF-THEN-ELSE 分支結構,Prolog可用兩條同頭的(同頭即指結論相同)并列規(guī)則實現(xiàn).例如,將:
IF X》0 THEN X:=1
ELSE X:=012
用 Prolog實現(xiàn)則是:
br :- X》 0, X = 1.
br :- X = 0.12
類似地,對于多分支,可以用多條規(guī)則實現(xiàn).例如:
br :- X》 0, X = 1.
br :- X = 0, X = 0.
br :- X 《 0, X = -1.123
2、循環(huán)
Prolog 可以實現(xiàn)計循環(huán)次數(shù)的 FOR 循環(huán),也可以實現(xiàn)不計循環(huán)次數(shù)的 DO 循環(huán).
舉個例子:
下面的程序段就實現(xiàn)了循環(huán),它使得 write 語句重復執(zhí)行了3次,而打印輸出了3個學生的記錄:
student(1, “張三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
print :- student(Number, Name, Score),
write(Number, Name, Score), nl,
Number = 3.123456
可以看出,程序第一次執(zhí)行,student 謂詞與第一個事實匹配,write 語句便輸出了張三同學的記錄.但當程序執(zhí)行到最后一句時,由于 Number 不等于 3,則該語句失敗,于是,引起回溯.而 write 語句和 nl 語句均只能執(zhí)行一次,所以繼續(xù)向上回溯到 student 語句.這樣,student 謂詞則因失敗而重新匹配.這一次與第二個事實匹配,結果輸出了李四的記錄.同理,當執(zhí)行到最后一句時又引起了回溯.write 語句第三次執(zhí)行后,由于 Number 已等于3,所以最后一個語句成功,程序段便運行結束.
這個例子可以看做是計數(shù)循環(huán).當然,也可以通過設置計數(shù)器而實現(xiàn)真正的計數(shù)循環(huán).下面的程序段實現(xiàn)的則是不計數(shù)的 DO 循環(huán):
student(1, “張三”, 90.2).
student(2, “李四”, 95.5).
student(3, “王五”, 96.4).
print :- student(Number, Name, Score),
write(Number, Name, Score), nl,
fail.
print :-.1234567
這個程序段中的 fail 是一個內部謂詞,它的語義是恒失敗.這個程序段與上面的程序段的差別僅在于把原來用計數(shù)器(或標記數(shù))進行循環(huán)控制的語句變成了恒失敗謂詞 fail,另外又增加了一個 print 語句,增加這個語句的目的是為程序設置一個出口.因為 fail 是恒失敗,下面若無出口的話,將引起 print 本身的失敗,進而又會導致程序的連鎖失敗.
還需說明的是,用 Prolog的遞歸機制也可以實現(xiàn)循環(huán),不過用遞歸實現(xiàn)循環(huán)通常需與表相配合.另外,遞歸的缺點是容易引起內存溢出,故通常的循環(huán)多是用上述方法實現(xiàn)的.
?動態(tài)數(shù)據(jù)庫
動態(tài)數(shù)據(jù)庫就是在內存中實現(xiàn)的動態(tài)數(shù)據(jù)結構.它由事實組成,程序可以對它操作,所以在程序運行期間它可以動態(tài)變化.Turbo Prolog 提供了 3 個動態(tài)數(shù)據(jù)庫操作謂詞,即:
asserta(《 fact》)
assertz(《 fact》)
retract(《 fact》)123
其中 fact 表示事實.這 3 個謂詞的功能如下:
asserta(《 fact》) 把 fact 插入當前動態(tài)數(shù)據(jù)庫中的同名謂詞的事實之前.
assertz(《 fact》) 把 fact 插入當前動態(tài)數(shù)據(jù)庫中的同名謂詞的事實之后.
retract(《 fact》) 把 fact 從當前動態(tài)數(shù)據(jù)庫中刪除.
例如語句:
asserta(student(20, “李明”, 90.5)).1
將在內存的謂詞名為 student 的事實前插入一個新事實:
student(20, ‘’李明“, 90.5)1
如果內存中還沒有這樣的事實,則它就是第一個.又如語句:
retract(student(20, _, _)).1
將從內存的動態(tài)數(shù)據(jù)庫中的刪除事實:
student(20, _, _)1
它可解釋為學號為 20 的一個學生的記錄.注意,這里用了無名變量 “_”.
可以看出,Turbo Prolog 提供的動態(tài)數(shù)據(jù)庫機制,可非常方便地實現(xiàn)堆棧、隊列等動態(tài)數(shù)據(jù)結構,提供的數(shù)據(jù)庫操作謂詞大大簡化了編程.
另外,Turbo Prolog 還提供了兩個文件操作謂詞:
save(《 filename》).
consult(《 filename》).12
其中 save 可將當前內存中的事實存入文件“filename”中,consult 則將文件“filename”中的事實調入內存.
評論