第一篇:邏輯型程序設(shè)計語言PROLOG詳細(xì)教程
邏輯型程序設(shè)計語言PROLOG教程
2.3.1邏輯型程序設(shè)計語言PROLOG PROLOG的語句
PROLOG語言只有三種語句,分別稱為事實、規(guī)則和問題。
1.事實(fact)
格式: <謂詞名>(<項表>).功能
一般表示對象的性質(zhì)或關(guān)系。
其中謂詞名是以小寫英文字母打頭的字母、數(shù)字、下劃線等組成的字符串,項表是以逗號隔開的項序列。
例如:
student(john).like(mary ,music).表示“約翰是學(xué)生”和“瑪麗喜歡音樂”。2.規(guī)則(rule)
格式:
<謂詞名>(<項表>):-<謂詞名>(<項表>){,<謂詞名>(<項表>)}.功能: 一般表示對象間的因果關(guān)系、蘊(yùn)含關(guān)系或?qū)?yīng)關(guān)系。
其中“:-”號表示“if”(也可以直接寫為if),其左部的謂詞是規(guī)則的結(jié)論(亦稱為頭),右部的謂詞是規(guī)則的前提(亦稱為體),{}表示零次或多次重復(fù),逗號表示and(邏輯與),即規(guī)則的形式是一個邏輯蘊(yùn)含式。
例如:
bird(X):-animal(X),has(X,feather).grandfather(X,Y):-father(X,Z),father(Z,Y).第一條規(guī)則表示“如果X是動物,并且X有羽毛,則X是鳥”;第二條規(guī)則就表示“X是Y的祖父,如果存在Z,X是Z的父親并且Z又是Y的父親”。
3.問題(question)
格式: ?-<謂詞名>(<項表>){,<謂詞名>(<項表>)}.功能 表示用戶的詢問,它就是程序運行的目標(biāo)。
例如:
?-student(john).?-like(mary,X).2.3.2 PROLOG程序
PROLOG程序一般由一組事實、規(guī)則和問題組成。問題是程序執(zhí)行的起點,稱為程序的目標(biāo)。
例如下面就是一個PROLOG程序。
likes(bell,sports).likes(mary,music).likes(mary,sports).likes(jane ,smith).friend(john,X):-likes(X,reading),likes(X,music).friend(john,X):-likes(X,sports),likes(X,music).?-friend(john,Y).可以看出,這個程序中有四條事實、兩條規(guī)則和一個問題。其中事實、規(guī)則和問題都分行書寫。規(guī)則和事實可連續(xù)排列在一起,其順序可隨意安排,但同一謂詞名的事實或規(guī)則必須集中排列在一起。問題不能與規(guī)則及事實排在一起,它作為程序的目標(biāo)要么單獨列出,要么在程序運行時臨時給出。
這個程序的事實描述了一些對象(包括人和事物)間的關(guān)系;而規(guī)則則描述了john交朋友的條件,即如果一個人喜歡讀書并且喜歡音樂(或者喜歡運動和喜歡音樂),則這個人就是john的朋友(當(dāng)然,這個規(guī)則也可看作是john朋友的定義);程序中的問題是“約翰的朋友是誰?”
當(dāng)然,PROLOG程序中的目標(biāo)可以變化,也可以含有多個語句(上例中只有一個)。如果有多個語句,則這些語句稱為子目標(biāo)。例如對上面的程序,其問題也可以是 ?-likes(mary,X).或
?-likes(mary,music).或
?-friend(X,Y).或
?-likes(bell,sports), likes(mary,music), friend(john,X).等等。當(dāng)然,對于不同的問題,程序運行的結(jié)果一般是不一樣的。
2.3.3 PROLOG程序的運行機(jī)理
PROLOG程序的運行是從目標(biāo)出發(fā),并不斷進(jìn)行匹配、合一、歸結(jié),有時還要回溯,直到目標(biāo)被完全滿足或不能滿足時為止。1.自由變量與約束變量
PROLOG中稱無值的變量為自由變量,有值的變量為約束變量。一個變量取了某值就說該變量約束于某值,或者說該變量被某值所約束,或者說該變量被某值實例化了。
2.匹配合一
兩個謂詞可匹配合一,是指兩個謂詞的名相同,參量項的個數(shù)相同,參量類型對應(yīng)相同,并且對應(yīng)參量項還滿足下列條件之一:
(1)如果兩個都是常量,則必須完全相同。
(2)如果兩個都是約束變量,則兩個約束值必須相同。
(3)如果其中一個是常量,一個是約束變量,則約束值與常量必須相同。
(4)至少有一個是自由變量。例如:下面的兩個謂詞
pre1(“ob1”,“ob2”,Z)
pre1(“ob1”,X,Y)
只有當(dāng)變量X被約束為“ob2”,且Y、Z的約束值相同或者至少有一個是自由變量時,它們才是匹配合一的。
3.回溯
所謂回溯,就是在程序運行期間,當(dāng)某一個子目標(biāo)不能滿足(即謂詞匹配失敗)時,控制就返回到前一個已經(jīng)滿足的子目標(biāo)(如果存在的話),并撤消其有關(guān)變量的約束值,然后再使其重新滿足。成功后,再繼續(xù)滿足原子目標(biāo)。如果失敗的子目標(biāo)前再無子目標(biāo),則控制就返回到該子目標(biāo)的上一級目標(biāo)(即該子目標(biāo)謂詞所在規(guī)則的頭部)使它重新匹配。回溯也是PROLOG的一個重要機(jī)制。
下面,我們介紹PROLOG程序的運行過程。我們?nèi)砸陨厦娴某绦驗槔?。設(shè)所給的詢問是
?-friend(john,Y).(john和誰是朋友?)
則求解目標(biāo)為
friend(john,Y).這時,系統(tǒng)對程序進(jìn)行掃描,尋找能與目標(biāo)謂詞匹配合一的事實或規(guī)則頭部。顯然,程序中前面的四條事實均不能與目標(biāo)匹配,而第五個語句的左端即規(guī)則
friend(john,X):-likes(X,reading),likes(X,music).的頭部可與目標(biāo)謂詞匹配合一。但由于這個語句又是一個規(guī)則,所以其結(jié)論要成立則必須其前提全部成立。于是,對原目標(biāo)的求解就轉(zhuǎn)化為對新目標(biāo)
likes(X,reading),likes(X,music).的求解。這實際是經(jīng)歸結(jié),規(guī)則頭部被消去,而目標(biāo)子句變?yōu)?/p>
?-likes(X,reading),likes(X,music).現(xiàn)在依次對子目標(biāo)
likes(X,reading)和likes(X,music)
求解。
子目標(biāo)的求解過程與主目標(biāo)完全一樣,也是從頭對程序進(jìn)行掃描,不斷進(jìn)行測試和匹配合一等,直到匹配成功或掃描完整個程序為止??梢钥闯?,對第一個子目標(biāo)like(X,reading)的求解因無可匹配的事實和規(guī)則而立即失敗,進(jìn)而導(dǎo)致規(guī)則
friend(john,X):-likes(X,reading),likes(X,music).的整體失敗。于是,剛才的子目標(biāo)
likes(X,reading)和likes(X,music)
被撤消,系統(tǒng)又回溯到原目標(biāo)friend(john,X)。這時,系統(tǒng)從該目標(biāo)剛才的匹配語句處(即第五句)向下繼續(xù)掃描程序中的子句,試圖重新使原目標(biāo)匹配,結(jié)果發(fā)現(xiàn)第六條語句的左部,即規(guī)則
friend(john,X):-likes(X,sports),likes(X,music).的頭部可與目標(biāo)為謂詞匹配。但由于這個語句又是一個規(guī)則,于是,這時對原目標(biāo)的求解,就又轉(zhuǎn)化為依次對子目標(biāo)
likes(X,sports)和likes(X,music) 的求解。這次子目標(biāo)likes(X,sports)與程序中的事實立即匹配成功,且變量X被約束為bell。于是,系統(tǒng)便接著求解第二個子目標(biāo)。由于變量X已被約束,所以這時第二個子目標(biāo)實際上已變成了
likes(bell,music).由于程序中不存在事實likes(bell,music),所以該目標(biāo)的求解失敗。于是,系統(tǒng)就放棄這個子目標(biāo),并使變量X恢復(fù)為自由變量,然后回溯到第一個子目標(biāo),重新對它進(jìn)行求解。由于系統(tǒng)已經(jīng)記住了剛才已同第一子目標(biāo)謂詞匹配過的事實的位置,所以重新求解時,便從下一個事實開始測試。
易見,當(dāng)測試到程序中第三個事實時,第一個子目標(biāo)便求解成功,且變量X被約束為mary。這樣,第二個子目標(biāo)也就變成了
likes(mary,music).再對它進(jìn)行求解。這次很快成功。
由于兩個子目標(biāo)都求解成功,所以,原目標(biāo)friend(john,Y)也成功,且變量Y被約束為mary(由Y與X的合一關(guān)系)。于是,系統(tǒng)回答:
Y=mary
程序運行結(jié)束。
上面只給出了問題的一個解。如果需要和可能的話,系統(tǒng)還可把john的所有朋友都找出來。我們把上述程序的運行過程再用示意圖(圖2─1)描述如下:
圖2─1
PROLOG程序運行機(jī)理示例
上述程序的運行是一個通過推理實現(xiàn)的求值過程。我們也可以使它變?yōu)樽C明過程。例如,把上述程序中的詢問改為
friend(john,mary)
則系統(tǒng)會回答:yes
若將詢問改為:
riend(john,smith)
則系統(tǒng)會回答:no
從上述程序的運行過程可以看出,PROLOG程序的執(zhí)行過程是一個(歸結(jié))演繹推理過程。其特點是:推理方式為反向推理,控制策略是深度優(yōu)先,且有回溯機(jī)制。其具體實現(xiàn)方法是:匹配子句的順序是自上而下;子目標(biāo)選擇順序是從左向右;(歸結(jié)后)產(chǎn)生的新子目標(biāo)總是插入被消去的目標(biāo)處(即目標(biāo)隊列的左部)。PROLOG的這種歸結(jié)演繹方法被稱為SLD(LinearresolutionwithSelectionfunctionforDefiniteclause)歸結(jié),或SLD反駁-消解法。SLD歸結(jié)就是PROLOG程序的運行機(jī)理,它也就是所謂的PROLOG語言的過程性語義。2.4
Turbo PROLOG程序設(shè)計 2.4.1 Turbo PROLOG的程序結(jié)構(gòu)
一個完整的Turbo PROLOG(2.0版)程序一般包括常量段、領(lǐng)域段、數(shù)據(jù)庫段、謂詞段、目標(biāo)段和子句段等六個部分。各段以其相應(yīng)的關(guān)鍵字constants、domains、database、predicates、goal和clauses開頭加以標(biāo)識。:
另外,在程序的首部還可以設(shè)置指示編譯程序執(zhí)行特定任務(wù)的編譯指令;在程序的任何位置都可設(shè)置注解??傊?,一個完整的TurboPROLOG(2.0版)程序的結(jié)構(gòu)如下
/*<注釋>*/
<編譯指令>
constants
<常量說明>
domains
<域說明>
database
<數(shù)據(jù)庫說明>
predicates
<謂詞說明>
goal
<目標(biāo)語句>
clauses
<子句集>
當(dāng)然,一個程序不一定要包括上述所有段,但一個程序至少要有一個predicates段、clauses段和goal段。在大多數(shù)情形中,還需要一個domains段,以說明表、復(fù)合結(jié)構(gòu)及用戶自定義的域名。如若省略goal段,則可在程序運行時臨時給出,但這僅當(dāng)在開發(fā)環(huán)境中運行程序時方可給出。若要生成一個獨立的可執(zhí)行文件,則在程序中必須包含goal段。另一方面,一個程序也只能有一個goal段。
例2.3 如果把上節(jié)中的程序要作為TurboPROLOG程序,則應(yīng)改寫為:
/*例子程序-1*/
DOMAINS
name=symbol
PREDICATES
likes(name,name).friend(name,name)
GOAL
friend(john,Y),write(″Y=″,Y).CLAUSES likes(bell,sports).likes(mary,music).likes(mary,sports).likes(jane,smith).friend(john,X):-likes(X,sports),likes(X,music).friend(john,X):-likes(X,reading),likes(X,music).結(jié)合上例,我們再對上述程序結(jié)構(gòu)中的幾個主要段的內(nèi)容和作用加以說明(其余段在后面用到時再作說明):
領(lǐng)域段該段說明程序謂詞中所有參量項所屬的領(lǐng)域。領(lǐng)域的說明可能會出現(xiàn)多層說明,直到最終說明到Turbo PROLOG的標(biāo)準(zhǔn)領(lǐng)域為止(如上例所示)。Turbo PROLOG的標(biāo)準(zhǔn)領(lǐng)域即標(biāo)準(zhǔn)數(shù)據(jù)類型,包括整數(shù)、實數(shù)、符號、串和符號等,其具體說明如表2.1所示。
表2.1 Turbo PROLOG的標(biāo)準(zhǔn)領(lǐng)域
謂詞段:該段說明程序中用到的謂詞的名和參量項的名(但Turbo PROLOG的內(nèi)部謂詞無須說明)。
子句段:該段是Turbo PROLOG程序的核心,程序中的所有事實和規(guī)則就放在這里,系統(tǒng)在試圖滿足程序的目標(biāo)時就對它們進(jìn)行操作。
目標(biāo)段:該段是放置程序目標(biāo)的地方。目標(biāo)段可以只有一個目標(biāo)謂詞,例如上面的例子中就只有一個目標(biāo)謂詞;也可以含有多個目標(biāo)謂詞,如:
goal
readint(X),Y=X+3,write(“Y=”,Y).就有三個目標(biāo)謂詞。這種目標(biāo)稱為復(fù)合目標(biāo)。
另外,一般稱程序目標(biāo)段中的目標(biāo)為內(nèi)部目標(biāo),而稱在程序運行時臨時給出的目標(biāo)為外部目標(biāo)。
2.4.2 Turbo PROLOG的數(shù)據(jù)與表達(dá)式 1.領(lǐng)域
1)標(biāo)準(zhǔn)領(lǐng)域
Turbo PROLOG中不定義變量的類型,只說明謂詞中各個項的取值域。2)結(jié)構(gòu)
結(jié)構(gòu)也稱復(fù)合對象,它是TurboPROLOG謂詞中的一種特殊的參量項(類似于謂詞邏輯中的函數(shù))。
結(jié)構(gòu)的一般形式為
<函子>(<參量表>)
其中函子及參量的標(biāo)識符與謂詞相同。注意,這意味著結(jié)構(gòu)中還可包含結(jié)構(gòu)。所以,復(fù)合對象可表達(dá)樹形數(shù)據(jù)結(jié)構(gòu)。例如下面的謂詞
likes(Tom,sports(football,basketball,table-tennis)).中的
sports(football,basketball,table-tennis)
就是一個結(jié)構(gòu),即復(fù)合對象。
又如:
person(“張華”,student(“西安石油學(xué)院”),address(“中國”,“陜西”,“西安”)).reading(“王宏”,book(“人工智能技術(shù)基礎(chǔ)教程”,“西安電子科技大學(xué)出版社”)).friend(father(“Li”),father(“Zhao”)).這幾個謂詞中都有復(fù)合對象。復(fù)合對象在程序中的說明,需分層進(jìn)行。例如,對于上面的謂詞
likes(Tom,sports(football,basketball,table-tennis)).在程序中可說明如下:
domains
name=symbol
sy=symbol
sp=sports(sy,sy,sy)
predicates
likes(name,sp)3)表
表的一般形式是
[x1,x2,…,xn]
其中xi(i=1,2,…,n)為PROLOG的項,一般要求同一個表的元素必須屬于同一領(lǐng)域。
不含任何元素的表稱為空表,記為[]。例如下面就是一些合法的表。
[1,2,3]
[apple,orange,banana,grape,cane]
[“PROLOG”,“MAENS”,“PROGRAMMING”,“in logic”] [[a,b],[c,d],[e]] []
表的最大特點是其元素個數(shù)可在程序運行期間動態(tài)變化。表的元素也可以是結(jié)構(gòu)或表,且這時其元素可以屬于不同領(lǐng)域。
例如:
name(“Li Ming”),age(20),sex(male),address(xi an)] [[1,2],[3,4,5],[6,7]]
都是合法的表。后一個例子說明,表也可以嵌套。
實際上,表是一種特殊的結(jié)構(gòu)。它是遞歸結(jié)構(gòu)的另一種表達(dá)形式。這個結(jié)構(gòu)的函數(shù)名取決于具體的PROLOG版本。這里我們就用一個圓點來表示。
下面就是一些這樣的結(jié)構(gòu)及它們的表表示形式:
結(jié)構(gòu)形式
表形式 ·(a,[])
[a] ·(a,·(b,[]))
[a,b] ·(a,·(b,·(c,[])))
[a,b,c]
表的說明方法是在其組成元素的說明符后加一個星號*。如:
domains
lists=string*
predicates
pl(lists)
就說明謂詞pl中的項lists是一個由串string組成的表。
對于由結(jié)構(gòu)組成的表,至少得分三步說明。例如對于下面謂詞p中的表
p([name(“Liming”),age(20)])
則需這樣說明:
domains
rec=seg*
seg=name(string);age(integer)
predicates
p(rec)2.常量與變量
由上面的領(lǐng)域可知,Turbo PROLOG的常量有整數(shù)、實數(shù)、字符、串、符號、結(jié)構(gòu)、表和文件這八種數(shù)據(jù)類型。同理,Turbo PROLOG的變量也就有這八種取值。另外,變量名要求必須是以大寫字母或下劃線開頭的字母、數(shù)字和下劃線序列,或者只有一個下劃線。這后一種變量稱為無名變量。3.算術(shù)表達(dá)式
Turbo PROLOG提供了五種最基本的算術(shù)運算:加、減、乘、除和取模,相應(yīng)運算符號為+、-、*、/、mod。這五種運算的順序為:*、/、mod優(yōu)先于+、-。同級從左到右按順序運算,括號優(yōu)先。算術(shù)表達(dá)式的形式與數(shù)學(xué)中的形式基本一樣。例如:
數(shù)學(xué)中的算術(shù)表達(dá)式
PROLOG中的算術(shù)表達(dá)式
x+yz
X+Y*Z
ab-c/d
A*B-C/D
u mod v
U mod V(表示求U除以V所得的余數(shù))即是說,Turbo PROLOG中算術(shù)表達(dá)式采用通常數(shù)學(xué)中使用的中綴形式。這種算術(shù)表達(dá)式為PROLOG的一種異體結(jié)構(gòu),若以PROLOG的結(jié)構(gòu)形式來表示,則它們應(yīng)為
+(X,*(Y,Z))
-(*(A,B),/(C,D))
mod(U,V)
所以,運算符+、-、*、/和mod實際也就是PROLOG內(nèi)部定義好了的函數(shù)符。
在Turbo PROLOG程序中,如果一個算術(shù)表達(dá)式中的變元全部被實例化(即被約束)的話,則這個算術(shù)表達(dá)式的值就會被求出。求出的值可用來實例化某變量,也可用來同其他數(shù)量進(jìn)行比較,用一個算術(shù)表達(dá)式的值實例化一個變量的方法是用謂詞“is”或“=”來實現(xiàn)。例如:
Y is X+5 或 Y=X+5
(*)
就使變量Y實例化為X+5的值(當(dāng)然X也必須經(jīng)已被某值實例化),可以看出,這里對變量Y的實例化方法類似于其他高級程序語言中的“賦值”,但又不同于賦值。例如,在PROLOG中下面的式子是錯誤的:
X=X+1 4.關(guān)系表達(dá)式
Turbo PROLOG提供了六種常用的關(guān)系運算,即小于、小于或等于、等于、大于、大于或等于和不等于,其運算符依次為
<,<=,=,>,>=,<>
Turbo PROLOG的關(guān)系表達(dá)式的形式和數(shù)學(xué)中的也基本一樣,例如:
數(shù)學(xué)中的關(guān)系式
Turbo PROLOG中的關(guān)系式
X+1≥Y
X+1>=Y
X≠Y
X<>Y 即是說,Turbo PROLOG中的關(guān)系式也用中綴形式。當(dāng)然,這種關(guān)系式為Turbo PROLOG中的異體原子。若按Turbo PROLOG中的原子形式來表示,則上面的兩個例子為
>=(X+1,Y)和<>(X,Y)
所以上述六種關(guān)系運算符,實際上也就是Turbo PROLOG內(nèi)部定義好了的六個謂詞。這六個關(guān)系運算符可用來比較兩個算術(shù)表達(dá)式的大小。
例如:
brother(Name1,Name2):-person(Name1,man,Age1),person(Name2,man,Age2),mother(Z,Name1),mother(Z,Name2),Age1>Age2.需要說明的是,“=”的用法比較特殊,它既可以表示比較,也可以表示約束值,即使在同一個規(guī)則中的同一個“=”也是如此。
例如:
(例一)
p(X,Y,Z):-Z=X+Y.當(dāng)變量X、Y、Z全部被實例化時,“=”就是比較符。如:對于問題
Goal:p(3,5,8).機(jī)器回答:yes。而對于
Goal:p(3,5,7).機(jī)器回答:no。
即這時機(jī)器把X+Y的值,與Z的值進(jìn)行比較。
(例二)但當(dāng)X,Y被實例化,為Z未被實例化時,“=”號就是約束符。如:
Goal:p(3,5,Z).機(jī)器回答:Z=8 這時,機(jī)器使Z實例化為X+Y的結(jié)果。2.4.3 輸入與輸出
雖然PROLOG能自動輸出目標(biāo)子句中的變量的值,但這種輸出功能必定有限,往往不能滿足實際需要;另一方面,對通常大多數(shù)的程序來說,運行時從鍵盤上輸入有關(guān)數(shù)據(jù)或信息也是必不可少的。為此每種具體PROLOG一般都提供專門的輸入和輸出謂詞,供用戶直接調(diào)用。例如,下面就是TorboPROLOG的幾種輸入輸出謂詞:
(1)readln(X)。
這個謂詞的功能是從鍵盤上讀取一個字符串,然后約束給變量X。
(2)readint(X)。
這個謂詞的功能是從鍵盤上讀取一個整數(shù),然后約束給變量X,如果鍵盤上打入的不是整數(shù)則該謂詞失敗。
(3)readreal(X)。
這個謂詞的功能是從鍵盤上讀取一個實數(shù),然后約束給變量X,如果鍵盤上打入的不是實數(shù)則該謂詞失敗。
(4)readchar(X)。
這個謂詞的功能是從鍵盤上讀取一個字符,然后約束給變量X,如果鍵盤上打入的不是單個字符,則該謂詞失敗。
(5)write(X1,X2,… Xn)。
這個謂詞的功能是把項Xi(i=1,2,…n)的值顯示在屏幕上或者打印在紙上,當(dāng)有某個Xi未實例化時,該謂詞失敗,其中的Xi可以是變量,也可以是字符串或數(shù)字。
(6)nl換行謂詞。它使后面的輸出(如果有的話)另起一行。另外,利用write的輸出項“n”也同樣可起換行作用。例如:
write(“name”), n l ,write(“age”)
與write(“name”,“n”,“age”)的效果完全一樣。例2.4用上面的輸入輸出謂詞編寫一個簡單的學(xué)生成績數(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(“對不起,找不到這個學(xué)生!”).grade:-write(“對不起,找不到這個學(xué)生!”).下面是程序運行時的屏幕顯示: 請輸入姓名: 王五
王五的成績是96.4。
2.4.4 分支與循環(huán)
PROLOG中并無專門的分支和循環(huán)語句,但PROLOG也可實現(xiàn)分支和循環(huán)程序結(jié)構(gòu)。1.分支
對于通常的IF-THEN-ELSE分支結(jié)構(gòu),PROLOG可用兩條同頭的并列規(guī)則實現(xiàn)。例如,將
IF x>0THENx:=1
ELSE x:=0 用PROLOG實現(xiàn)則是 Br :-x>0,x=1.Br :-x=0.類似地,對于多分支,可以用多條規(guī)則實現(xiàn)。例如: Br :-x>0,x=1.Br :-x=0,x=0.Br :-x<0,x=-1.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語句重復(fù)執(zhí)行了三次,而打印輸出了三個學(xué)生的記錄。
student(1,“張三”,90.2).student(2,“李四”,95.5).student(3,“王五”,96.4).print:-student(Number,Name,Score),write(Number,Name,Score),n l ,Number=3.這個例子可以看作是計數(shù)循環(huán)。當(dāng)然,也可以通過設(shè)置計數(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:-.這個程序段中的fail是一個內(nèi)部謂詞,它的語義是恒失敗。這個程序段與上面的程序段的差別僅在于把原來用計數(shù)器(或標(biāo)記數(shù))循環(huán)控制語句變成了恒失敗謂詞fail,另外再增加了一個print語句。增加這個語句的目的是為程序設(shè)置一個出口。因為fail是恒失敗,下面若無出口的話,將引起print本身的失敗。進(jìn)而又會導(dǎo)致程序中的連鎖失敗。
2.4.5 動態(tài)數(shù)據(jù)庫
動態(tài)數(shù)據(jù)庫就是在內(nèi)存中實現(xiàn)的動態(tài)數(shù)據(jù)結(jié)構(gòu)。它由事實組成,程序可以對它操作,所以在程序運行期間它可以動態(tài)變化。Turbo PROLOG提供了三個動態(tài)數(shù)據(jù)庫操作謂詞: asserta(
asserta(
assertz(
retract(
例如語句
asserta(student(20,“李明”,90.5)).將在內(nèi)存的謂詞名為student的事實前插入一個新事實:
student(20,“李明”,90.5)
如果內(nèi)存中還沒有這樣的事實,則它就是第一個。又如語句
retract(student(20,-,-)).將從內(nèi)存的動態(tài)數(shù)據(jù)庫中的刪除事實
student(20,-,-)它可解釋為學(xué)號為20的一個學(xué)生的記錄。注意,這里用了無名變量-。
可以看出,PROLOG提供的動態(tài)數(shù)據(jù)庫機(jī)制,可非常方便地實現(xiàn)堆棧、隊列等動態(tài)數(shù)據(jù)結(jié)構(gòu),提供的數(shù)據(jù)庫操作謂詞大大簡化了編程。
另外,PROLOG還提供了謂詞
save(
2.4.6 表處理與遞歸
表是PROLOG中一種非常有用的數(shù)據(jù)結(jié)構(gòu)。表的表述能力很強(qiáng),數(shù)字中的序列、集合,通常語言中的數(shù)組、記錄等均可用表來表示。表的最大特點是其長度不固定,在程序的運行過程中可動態(tài)地變化。具體來講,就是在程序運行時,可對表施行一些操作,如給表中添加一個元素,或從中刪除一個元素,或者將兩個表合并為一個表等等。用表還可以方便地構(gòu)造堆棧、隊列、鏈表、樹等動態(tài)數(shù)據(jù)結(jié)構(gòu)。
表還有一個重要特點,就是它可分為頭和尾兩部分。表頭是表中第一個元素,而表尾是表中除第一個元素外的其余元素按原來順序組成的表。例如下面的例子:
表
表頭
表尾
[1,2,3,4,5]
[2,3,4,5]
[apple,orange,banana]
apple
[orange,banana]
[[a,b],[c],[d,e]]
[a,b]
[[c],[d,e]]
[“PROLOG”]
“PROLOG“
[]
[]
無定義
無定義
在程序中是用豎線“|”來區(qū)分表頭和表尾的,而且還可以使用變量。例如一般地用[H|T]來表示一個表,其中H、T都是變量,H為表頭,T為表尾。注意,此處H是一個元素(表中第一個元素),而T則是一個表(除第一個元素外的表中其余元素按原來順序組成的表)。表的這種表示法很有用,它為表的操作提供了極大的方便。下面我們就給出用這種表示法通過匹配合一提取表頭和表尾的例子。
表1
表2
合一后的變量值 [X|Y]
[a,b,c]
X=a,Y=[b,c] [X|Y]
[a]
X=a,Y=[] [a|Y]
[X,b]
X=a,Y=[b] [X,Y,Z]
[a,b,c]
X=a,Y=b,Z=c [[a,Y]|Z]
[[X,b],[c]]
X=a,Y=b,Z=[[c]]
還需說明的是,表中的豎杠“|”后面只能有一個變量。例如寫法[X|Y,Z]就是錯誤的。但豎杠的前面的變量可以多于一個。例如寫法[X,Y|Z]是允許的。這樣,這個表同[a,b,c]匹配合一后,有
X=a,Y=b,Z=[c]
另外,豎杠的前面和后面也可以是常量,例如[a|Y]和[X|b]都是允許的,但注意,后一個表稱為無尾表,如果它同表[a|Y]匹配,則有
X=a,Y=b
(而不是Y=[b])
如果無豎杠“|”,則不能分離出表尾。例如,表[X,Y,Z]與[a,b,c]合一后得X=a,Y=b,Z=c。其中變量Z并非等于[c]。
例2.5 設(shè)計一個能判斷對象X是表L的成員的程序。
我們可以這樣設(shè)想:
(1)如果X與表L中的第一個元素(即表頭)是同一個對象,則X就是L的成員;
(2)如果X是L的尾部的成員,則X也就是L的成員。
根據(jù)這種邏輯關(guān)系,于是有下面的PROLOG程序:
member(X,[X|Tail]).member(X,[Head|Tail]):-member(X,Tail).
其中第一個子句的語義就是上面的第一句話,第二個子句的語義就是上面的第二句話。可以看出,這個程序中使用了遞歸技術(shù),因為謂詞member的定義中又含有它自身。利用這個程序我們就可以判定任意給定的一個對象和一個表之間是否具有member(成員)關(guān)系。
例如,我們?nèi)”鞮為[a,b,c,d],取X為a,對上面的程序提出如下詢問:
Goal:member(a,[a,b,c,d]).
則有回答:yes
同樣對于詢問:
Goal:member(b,[a,b,c,d]).Goal:member(c,[a,b,c,d]).Goal:member(d,[a,b,c,d]).
都有回答:yes
但若詢問
Goal:member(e,[a,b,c,d]).
則回答:no
如果我們這樣詢問
Goal:member(X,[a,b,c,d]).
意思是要證明存在這樣的X,它是該表的成員,這時系統(tǒng)返回X的值,即
X=a
如果需要的話,系統(tǒng)還會給出X的其他所有值。
例2.6 表的拼接程序,即把兩個表連接成一個表。
append([],L,L).append([H|T],L2,[H|Tn]):-append(T,L2,Tn).
程序中第一個子句的意思是空表同任一表L拼接的結(jié)果仍為表L;第二個子句的意思是說,一個非空的表L1與另一個表L2拼接的結(jié)果L3是這樣一個表,它的頭是L1的頭,它的尾是由L1的尾T同L2拼接的結(jié)果Tn。這個程序刻劃了兩個表與它們的拼接表之間的邏輯關(guān)系。
可以看出,謂詞append是遞歸定義的,子句append([],L,L).為終結(jié)條件,即遞歸出口。
對于這個程序,如果我們詢問
Goal:append([1,2,3],[4,5],L).
則系統(tǒng)便三次遞歸調(diào)用程序中的第二個子句,最后從第一個子句終止,然后反向依次求出每次的拼接表,最后輸出
L=[1,2,3,4,5]
當(dāng)然,對于這個程序也可以給出其他各種詢問,如: Goal:append([1,2,3],[4,5],[1,2,3,4,5]). 系統(tǒng)回答:yes Goal:append([1,2,3],[4,5],[1,2,3,4,5,6]). 系統(tǒng)回答:no Goal:append([1,2,3],Y,[1,2,3,4,5]). 系統(tǒng)回答:Y=[4,5]
Goal:append(X,[4,5],[1,2,3,4,5]).
系統(tǒng)回答:X=[1,2,3]
Goal:append(X,Y,[1,2,3,4,5]). 系統(tǒng)回答: X=[],Y=[1,2,3,4,5] X=[1],Y=[2,3,4,5] X=[1,2],Y=[3,4,5] X=[1,2,3],Y=[4,5] …
等等(如果需要所有解的話)。
例2.7 表的輸出。
print([]).print([H|T]):-write(H),print(T).例2.8 表的倒置,即求一個表的逆序表。
reverse([],[]).reverse([H|T],L):-reverse(T,L1),append(L1,[H],L).
這里,reverse的第一個項是輸入,即原表,第二個項是輸出,即原表的倒置。2.4.7 回溯控制
PROLOG在搜索目標(biāo)解的過程中,具有回溯機(jī)制,即當(dāng)某一個子目標(biāo)Gi不能滿足時,就返回到該子目標(biāo)的前一個子目標(biāo)Gi-1,并放棄Gi-1的當(dāng)前約束值,使它重新匹配合一。在實際問題中,有時卻不需要回溯,為此PROLOG中就專門定義了一個阻止回溯的內(nèi)部謂詞——“!”,稱為截斷謂詞。
截斷謂詞的語法格式很簡單,就是一個感嘆號“!”。!的語義是:
(1)若將“!”插在子句體內(nèi)作為一個子目標(biāo),它總是立即成功;
(2)若“!”位于子句體的最后,則它就阻止對它所在子句的頭謂詞的所有子句的回溯訪問,而讓回溯跳過該頭謂詞(子目標(biāo)),去訪問前一個子目標(biāo)(如果有的話);
(3)若“!”位于其他位置,則當(dāng)其后發(fā)生回溯且回溯到“!”處時,就在此處失敗,并且“!”還使它所在子句的頭謂詞(子目標(biāo))整個失?。醋柚乖偃ピL問頭謂詞的其余子句(如果有的話),即迫使系統(tǒng)直接回溯到該頭謂詞(子目標(biāo))的前一個子目標(biāo)(如果有的話))。
例2.9考慮下面的程序:
p(a).(2─1)
p(b).(2─2)
q(b).(2─3)
r(X):-p(X),q(X).(2─4)
r(c).
對于目標(biāo):r(Y).
可有一個解
Y=b
但當(dāng)我們把式(2─4)改為
r(X):-p(X),!,q(X).(2─4′)
時,卻無解。
這是由于添加了截斷謂詞“!”。因為式(2─4′)中求解子目標(biāo)p(X)時,X被約束到a,然后跳過“!”,但在求解子目標(biāo)q(a)時遇到麻煩,于是又回溯到“!”,而“!”阻止了對p(X)的下一個子句p(b)和r的下一個定義子句r(c)的訪問。從而,導(dǎo)致整個求解失敗。
例2.10 設(shè)有程序:
g0:-g11,g12,g13.(2─5)g0:-g14.(2─6)g12:-g21,!,g23.(2─7)g12:-g24,g25.(2─8).........
給出目標(biāo):g0.假設(shè)運行到子目標(biāo)g23時失敗,這時如果子句(2─7)中無!的話,則會回溯到g21,并且,如果g21也失敗的話,則會訪問下面的子句(2─8)。但由于有!存在,所以不能回溯到g21,而直接宣告g12失敗。于是,由子句(2─5),這時則回溯到g11。如果我們把子句(2─7)改為
g12:-g21, g23,!.(2─9)當(dāng)然這時若g23失敗時,便可回溯到g21,而當(dāng)g21也失敗時,便回溯到g12,即子句(2─8)被“激活”。但對于修改后的程序,如果g13失敗,則雖然可回溯到g12,但對g12不做任何事情,便立即跳過它,而回溯到g11,如果子句(2─9)中無!,則當(dāng)g13失敗時,回溯到g12便去考慮子句(2─8),只有當(dāng)子句(2─8)再失敗時才回溯到g11。2.4.8 程序舉例
下面我們給出幾個簡單而又典型的例子程序。通過這些程序,讀者可以進(jìn)一步體會和理解PROLOG程序的風(fēng)格和能力,也可以掌握一些基本的編程技巧。
例2.11 下面是一個簡單的路徑查詢程序。程序中的事實描述了如圖2─2所示的有向圖,規(guī)則是圖中兩節(jié)點間有通路的定義。
圖2─2
有向圖
predicates
road(symbol,symbol)
path(symbol,symbol)clauses
road(a,b).road(a,c).road(b,d).road(c,d).road(d,e).road(b,e).path(X,Y):-road(X,Y).path(X,Y):-road(X,Z),path(Z,Y).程序中未含目標(biāo),所以運行時需給出外部目標(biāo)。例如當(dāng)給目標(biāo):
path(a,e).
系統(tǒng)將回答:yes
但當(dāng)給目標(biāo):
path(e,a).
時,系統(tǒng)則回答:no
如果給出目標(biāo):
run. 且在程序中增加子句
run:-path(a,X),write(”X=“,X),nl,fail.run. 屏幕上將會輸出:
X=b
X=c
X=d
X=e
X=d
X=e
X=e
即從a出發(fā)到其他節(jié)點的全部路徑。
例2.12 下面是一個求階乘程序,程序中使用了遞歸。
/*aFactorialProgram*/
domains
n,f=integer
predicates
factorial(n,f)
goal
readint(I),factorial(I,F),write(I,”!=“,F).clauses
factorial(1,1).factorial(N,Res):-
N>0,N1=N-1,factorial(N1,FacN1),Res=N*FacN1.程序運行時,從鍵盤輸入一個整數(shù),屏幕上將顯示其階乘數(shù)。
例2.13 下面是一個表的排序程序,采用插入排序法。
/*insertsort*/
domains
listi=integer*
predicates
insert-sort(listi , listi)
insert(integer,listi,listi)
asc-order(integer,integer)
clauses
insert-sort([],[]).insert-sort([H|Tail],Sorted-list):-insert-sort(Tail,Sorted-Tail),insert(H,Sorted-Tail,Sorted-list).insert(X,[Y|Sorted-list],[Y|Sorted-list1]):-asc-order(X,Y),!,insert(X,Sorted-list,Sorted-list1).insert(X,Sorted-list,[X|Sorted-list]).asc-order(X,Y):-X>Y.程序中對表處理使用了遞歸。程序中也未給出目標(biāo),需要在運行時臨時給出。例如當(dāng)給目標(biāo):
insert-sort([5,3,4,2,6,1,7,8,9,0],L).系統(tǒng)將輸出:
L=[0,1,2,3,4,5,6,7,8,9]
例2.14下面是一個簡單的通信錄管理程序,其中用到輸入輸出、動態(tài)數(shù)據(jù)庫等。通過閱讀這個程序,我們還可以掌握循環(huán)結(jié)構(gòu)和簡單的菜單程序編寫方法。
/*通信錄*/
database
person(string,integer)
predicates
address-book
chose(integer)
input query
repeat goal
address-book.clauses
address-book:-repeat,clearwindow,write(”==============“),nl,write(”1--錄入“),nl, write(”2--查詢“),nl, write(”3--退出“),nl,write(”==============“),nl, write(”請選擇:-“), readint(N), chose(N).chose(1):-input,fail.chose(2):-query,fail.chose(3):-clearwindow,!.input:-clearwindow,write(”姓名:“),readln(Name),write(”電話:“),readint(Tel),assertz(person(Name,Tel)),!.query:-clearwindow,write(”姓名:“),readln(Name),person(Name,Tel),write(”電話:“,Tel),readchar(-),!.repeat.repeat:-repeat.程序中的repeat恒成功。它與內(nèi)部謂詞fail配合實現(xiàn)了循環(huán)。
需說明的是,這僅是一個演示性程序,還不能實用。因為這里的通信錄并未存入磁盤文件。用謂詞save就可方便地把通信錄存入磁盤文件。例如用語句
save(”addrbook.dat“)
就可把已插入內(nèi)存的person事實存入文件addrbook.dat中。而語句
consult(”addrbook.dat“)
則可又將該文件中的事實裝入內(nèi)存。
2.4 函數(shù)型程序設(shè)計語言LISP
LISP語言的主要特點是:
(1)LISP程序由一組函數(shù)組成,程序的執(zhí)行過程是函數(shù)的調(diào)用過程。
(2)程序和數(shù)據(jù)在形式上是相同的,即都是符號表達(dá)式,簡稱為S─表達(dá)式。
(3)遞歸是LISP語言的主要控制結(jié)構(gòu)。
(4)程序以交互方式運行。
2.2.1 LISP的程序結(jié)構(gòu)與運行機(jī)制
LISP的程序一般由函數(shù)的定義和函數(shù)的調(diào)用兩部分組成。其一般格式為:
(DEFUN(<函數(shù)名>(<形參表>)<函數(shù)體>)
(<函數(shù)名>(〖WB〗<形參表>)<函數(shù)體>)
…
(<函數(shù)名>(<形參表>)<函數(shù)體>))
(<函數(shù)名><實參表>)
(<函數(shù)名><實參表>)
…
(<函數(shù)名><實參表>)
其中的“DEFUN”是定義函數(shù)的關(guān)鍵字,“函數(shù)名”可以是系統(tǒng)的內(nèi)部函數(shù)(名),也可以是用戶用DEFUN定義的函數(shù)(名)。例如下面就是一個LISP程序。
(DEFUNHANOI(a b c n)
(COND((=n1)(MOVE-DISK a c))
(T(HANOI a c b(-n1))
(MOVE-DISK a c)
(HANOI b a c(-n1))))(DEFUNMOVE-DISK(from to)(TERPRI)(PRINC″Move Disk From″)(PRINC from)(PRINC”To“)(PRINC to))
(HANOI′a′b′c3)2.2.2 S─表達(dá)式
從語法上看,LISP程序的基本單位是S─表達(dá)式。S─表達(dá)式又可分為原子和表兩大類。原子(atom)是由字母和數(shù)字組成的字符串,是S─表達(dá)式的最簡單情況。原子又可分為文字原子、串原子和數(shù)字原子三種。
文字原子又稱符號(symbol),是以字母開頭的字母數(shù)字串,用來表示常量、變量和函數(shù)的名字等。例如:ABC、X1等。
串原子是由雙引號括起來的一串字符。如”LISP Program"。
數(shù)字原子由數(shù)字串組成。在其前面可以有符號“-”或“+”,中間可出現(xiàn)“.”,用來表示整數(shù)和實數(shù)。例如:256、-66、3.14159等。
S─表達(dá)式可以遞歸定義如下:
(1)原子是S─表達(dá)式。
(2)若S1和S2是S─表達(dá)式,則(S1·S2)也是S─表達(dá)式。由定義,下面的式子都是S─表達(dá)式:
X2
123
(A·B)
(A·(B·C))
表(list)是LISP語言中最常用的數(shù)據(jù)類型,也是主要的處理對象。表是由圓括號括起來的由空格分開的若干個元素的集合。
表的一般形式為:
(…)
例如:
(X Y Z),(+12),(A(B C))
左括號后面的第一個元素稱為表頭,其余的元素組成的表稱為表尾。例如,對于表
(+12)的頭為+,尾為(12)。
特別地,元素個數(shù)為零的表為空表,記為()或NIL。
表是一種特殊的S─表達(dá)式,每一個表都對應(yīng)著一個S─表達(dá)式。二者的關(guān)系由下面的例子說明。
表←——————————————→S-表達(dá)式
(A)
(A·NIL)
(AB)
(A·(B·NIL))
(ABC)
(A·(B·(C·NIL)))
((AB)CD)
((A·(B·NIL))·(C·(D·NIL)))
可以看出,表的S─表達(dá)式的結(jié)構(gòu)實際是一棵二叉樹。
2.2.3 基本函數(shù)
LISP的函數(shù)都以表的形式出現(xiàn),并一律使用前綴表示方式,即表頭為函數(shù)名,并且每個函數(shù)都有一個返回值。LISP的函數(shù)可分為語言自身提供的內(nèi)部函數(shù)(稱為基本函數(shù)或系統(tǒng)函數(shù))和用戶自定義函數(shù)兩類?;竞瘮?shù)的種類有十多個,下面僅給出其中主要的幾類。
1.表處理函數(shù)
表處理是LISP的主要特色,表處理的函數(shù)也很多,下面僅給出最常用的幾個。1)CAR函數(shù)
格式(CAR<表>)
其中CAR為函數(shù)名,它是一個保留字(下同)。功能取出表中的表頭。
例如:(CAR′(LISP Language Program))返回值為:LISP 2)CDR函數(shù)
格式(CDR<表>)
功能取出表中的表尾。
例如:(CDR′(LISP Language Program))
返回值為:(Language Program)
3)CONS函數(shù)
格式(CONS<表>)
功能將S─表達(dá)式作為一個元素加到表中去,并作為所構(gòu)成新表中的第一個元素。
例如:(CONS′My′(LISP Language Program))
返回值為:(My LISP Language Program)4)APPEND函數(shù)
格式(APPEND<表1><表2>…<表n>)功能
將n個表中的元素合并成一個新表。例如:(APPEND′(TIGER LION)′(DOG CAT))返回值為:(TIGER LION DOG CAT)5)LIST函數(shù)
格式(LIST…)功能把n個S─表達(dá)式作為元素括在一起構(gòu)成一張新表。例如:(LIST′YELLOW′RED′BLUE)返回值為:(YELLOW RED BLUE)
2.算術(shù)函數(shù)
LISP的算術(shù)表達(dá)式也是用函數(shù)表示的,稱為算術(shù)函數(shù)。下面我們僅舉例說明。
(+25)
表示2+5,返回值為7。
(-(*48)(/105))表示4×8-10/5,返回值為30。3.求值與賦值函數(shù)
在上面的函數(shù)中多次出現(xiàn)撇號′,它的意思是禁止求值。為什么要禁止求值呢?原來,LISP總是試圖對一切S─表達(dá)式求值。表的值是通過函數(shù)運算而得到的,原子的值則是通過賦值函數(shù)實現(xiàn)的。撇號′也是一個函數(shù),它實際是禁止求值函數(shù)QUOTE的簡寫形式。
賦值函數(shù)有多個,其中SET函數(shù)是一個最基本的賦值函數(shù)。
格式(SET<變量>)
功能把S─表達(dá)式賦給變量。
例如:
(SET′X′8);
X 得到值8
(SET′Y′(a b c));
Y 得到值(a b c)
(SET′Z(CDRY);
Z 得到值(b c)
另外,賦值函數(shù)還有SETQ、SETF(COMMON LISP),其功能是類似的。
4.謂詞函數(shù)
返回值為邏輯值真或假的函數(shù)稱為謂詞函數(shù),簡稱謂詞。LISP中真和假分別用T和NIL表示,當(dāng)函數(shù)的返回值為非NIL時,也表示為真。另外,NIL也表示空表。謂詞函數(shù)也有多個,下面我們僅給出常用的幾個。(1)原子謂詞ATOM
格式(ATOM<參數(shù)>)
功能檢測其參數(shù)是否為原子,是則返回T,否則返回NIL。
例如:
(ATOM′a);返回T
(ATOM′(a b));返回NIL(2)相等謂詞EQUAL
格式(EQUAL<參數(shù)><參數(shù)>)
功能判斷兩個參數(shù)是否邏輯相等。
例如:
(EQUAL′a′a);
返回T
(EQUAL′(a b)′(ac));
返回NIL
(EQUAL′(a b)(CONS′a′(b)));
返回T
還有一種相等謂詞,其格式為:(EQ<參數(shù)><參數(shù)>),但它只是用來判斷兩個原子是否相等。例如:(EQ′a′a),則返回T(3)判空表函數(shù)NULL
格式(NULL<參數(shù)>)
功能判斷參數(shù)是否為空表,是則返回T,否則返回NIL。
5.條件函數(shù)
條件函數(shù)也稱分支函數(shù),類似于其他語言中的分支語句,其作用是控制程序的流程。
格式
(COND(P1 e1)
(P2e2)
…
(Pnen))
其中Pi(i=1,...,n)為謂詞,ei(i=1,...,n)為一個或多個S─表達(dá)式。
功能如果P1為真,則COND函數(shù)的值為e1(當(dāng)e1為多個S─表達(dá)式時,取最后一個S─表達(dá)式的值,下同)。否則,判斷P2,……直到某個Pi真為止,然后將對應(yīng)的ei作為函數(shù)值。若沒有一個Pi的值為非NIL,則COND的返回值為NIL。特別地,Pi也可以為邏輯常量T,這時則對其對應(yīng)的各表達(dá)式求值,并把最后一個表達(dá)式的值作為COND的返回值。
例如:
(COND((NULL x)0)
((ATOM x)1)
((LISTP x)(LENGTH x)))
其語義是,若x的值為NIL,則COND的返回值為0;若x為原子,則COND的返回值為1;若x的值為表,則COND的返回值為表的長度。
2.2.4 自定義函數(shù)
基本函數(shù)是LISP提供的基本處理功能,要用LISP編程解決實際問題,僅有基本函數(shù)還是不夠的,用戶還必須根據(jù)問題的需要,利用基本函數(shù)自定義所需的函數(shù)。
自定義函數(shù)的格式為:
(DEFUN<函數(shù)名>(<形參表>)<函數(shù)體>)其中函數(shù)體,又可能是用戶自定義的函數(shù)或LISP基本函數(shù)的某種組合。所以,一般來講,LISP自定義函數(shù)就是由其基本函數(shù)組合而成的。常用的組合方法有復(fù)和、分支、遞歸、迭代等。其中最具特色的構(gòu)造方法是遞歸。
例2.1 定義求N!的LISP函數(shù)。
階乘的公式是
n!=n×(n-1)!
1!=1
0!=1
由此我們給出其LISP函數(shù)如下:
(DEFUNN!(n)
(COND((=n 0)1)
((=n 1)1)
(T(* n(N!(-n 1))))))可以看出,該函數(shù)的最后一行中又調(diào)用了它自己。所以,這個函數(shù)N!是遞歸定義的。
需說明的是,一個函數(shù)是否能遞歸定義,要取決于以下兩條:
(1)函數(shù)的求值存在最簡的情形,在這種情形下函數(shù)值是顯然的或已知的;
(2)該函數(shù)對于其參數(shù)的求值,可以歸結(jié)為對另一些參數(shù)的求值,而且后者比前者更容易求值,即使問題朝最簡情形逼近了一步。
2.2.5 程序舉例
例2.2 符號微分程序。
這里是指數(shù)學(xué)上的一元函數(shù)求導(dǎo)。我們用D(ex)表示數(shù)學(xué)上的de/dx,這里e為需求導(dǎo)的函數(shù)表達(dá)式,x為自變量。程序如下:
(DEFUND(ex)
(COND((ATOM e)(IF(Eq e x)1 0))
(T(APPLY(D-RULE(CAR e))
(APPEND(CDR e))
(LIST x)))))其中D-RULE是一個獲取給定操作符的微分規(guī)則的LISP函數(shù)。微分規(guī)則的存放,是通過為相應(yīng)操作符建立d特性的方法完成的。D-RULE的定義為
(DEFUN D-RULE(operator)
(GET operator′d))其中操作符d的特性值需事先用SETF函數(shù)建立好。例如對于操作符加+和乘·,在數(shù)學(xué)上有
d(u+v)/dx=du/dx+dv/dx
d(u·v)/dx=v·du/dx+u·dv/dx
用LISP表示就是
(SETF(GET′+′D)′(LAMBDA(u v
x)′(+,(Dux),(D v x))))
(SETF(GET′*′D)′(LAMBDA(u v
x)′(+(*,(Dux),v)(*,(D v x),u)))))有了這些函數(shù),我們就可以用機(jī)器求符號微分了。例如,給出如下的函數(shù)調(diào)用(D′(+(*2x)(*x x))′x);即求一元函數(shù)2x+x2關(guān)于x的導(dǎo)函數(shù)則得到返回值為
(+(+(* 0 x)(* 1 2))(+(* 1 x)(*1 x)))
即2+2x,結(jié)果正確。
由于篇幅所限,上面我們對LISP語言僅做了簡要介紹。需進(jìn)一步學(xué)習(xí)的讀者,可參閱有關(guān)專門著作。實際上,以此為入門和基礎(chǔ),讀者就可以參照某一具體的LISP語言資料,進(jìn)行LISP程序設(shè)計了。經(jīng)過30多年的發(fā)展,LISP的方言和版本也很多。目前比較流行的有INTERLISP、MACLISP、COMMONLISP。其中COMMONLISP將成為一種標(biāo)準(zhǔn),以統(tǒng)一各種LISP方言。
http://004km.cn/tags/Prolog/
第二篇:Excel函數(shù)教程:AND、OR、NOT邏輯函數(shù)
Excel函數(shù)教程:AND、OR、NOT邏輯函數(shù)
用來判斷真假值,或者進(jìn)行復(fù)合檢驗的Excel函數(shù),我們稱為邏輯函數(shù)。在Excel中提供了六種邏輯函數(shù)。即AND、OR、NOT、FALSE、IF、TRUE函數(shù)。
AND、OR、NOT這三個函數(shù)都用來返回參數(shù)邏輯值。詳細(xì)介紹見下:
(一)AND函數(shù)
所有參數(shù)的邏輯值為真時返回 TRUE;只要一個參數(shù)的邏輯值為假即返回 FALSE。簡言之,就是當(dāng)AND的參數(shù)全部滿足某一條件時,返回結(jié)果為TRUE,否則為FALSE。
語法為AND(logical1,logical2,...),其中Logical1, logical2,...表示待檢測的 1 到 30 個條件值,各條件值可能為TRUE,可能為FALSE。參數(shù)必須是邏輯值,或者包含邏輯值的數(shù)組或引用。舉例說明:
1、在B2單元格中輸入數(shù)字50,在C2中寫公式=AND(B2>30,B2<60)。由于B2等于50的確大于30、小于60。所以兩個條件值(logical)均為真,則返回結(jié)果為TRUE。
2、如果 B1-B3 單元格中的值為 TRUE、FALSE、TRUE,顯然三個參數(shù)并不都為真,所以 在B4單元格中的公式=AND(B1:B3)等于 FALSE
(二)OR函數(shù)
OR函數(shù)指在其參數(shù)組中,任何一個參數(shù)邏輯值為 TRUE,即返回 TRUE。它與AND函數(shù)的區(qū)別在于,AND函數(shù)要求所有函數(shù)邏輯值均為真,結(jié)果方為真。而OR函數(shù)僅需其中任何一個為真即可為真。比如,上面的示例2,如果在B4單元格中的公式寫為=OR(B1:B3)則結(jié)果等于TRUE
(三)NOT函數(shù)
NOT函數(shù)用于對參數(shù)值求反。當(dāng)要確保一個值不等于某一特定值時,可以使用 NOT 函數(shù)。簡言之,就是當(dāng)參數(shù)值為TRUE時,NOT函數(shù)返回的結(jié)果恰與之相反,結(jié)果為FALSE.比如NOT(2+2=4),由于2+2的結(jié)果的確為4,該參數(shù)結(jié)果為TRUE,由于是NOT函數(shù),因此返回函數(shù)結(jié)果與之相反,為FALSE。
第三篇:規(guī)范求職信寫作詳細(xì)教程
什么事求職信?求職信就是專門為某一職位的求職申請而寫,區(qū)別于求職簡歷,所以必須要有針對性,一般應(yīng)在信中提到所申請的公司名稱和職位名稱等信息。因為針對性強(qiáng)所以不可太長,這主要是考慮用人單位的時間問題,只要一頁就可以了。主要目的是向?qū)Ψ奖砻髂銓Ψ秸衅笍V告中的某個職位感興趣,極其概括陳述你的工作經(jīng)歷和經(jīng)驗合適你所申請的職位。
求職信的內(nèi)容寫法?
第一段引子,應(yīng)該說明你所應(yīng)聘的具體職位和得到職位信息的途徑,通常是簡短的一兩句話。對于一個大公司來講,每天收到的求職信會比較多,信的開頭就表明求職意向會給對方工作帶來方便。例如:
第二段描述部分,可以寫上求職者對所申請職位性質(zhì)的理解,對招聘公司的了解,可以展開說明你的應(yīng)聘優(yōu)勢。比如挑選 簡歷 中于所應(yīng)聘職位最相關(guān)的部分,促使招聘者想進(jìn)一步閱讀你的簡歷并有較強(qiáng)的針對性。描述部分的長度可以控制在兩到三個長句。例如:
“我是一個成功的銷售人員。在______公司任____ 職位的3年期間,曾數(shù)次因工作的主動性與創(chuàng)造性而受到獎勵。我負(fù)責(zé)的_____區(qū)域的___產(chǎn)品銷售市場占有率一直保持在__以上。我相信我在____領(lǐng)域里的市場銷售經(jīng)驗完全能夠勝任貴公司 一職。”
第三部分總結(jié),表明自己希望加入公司的誠意,禮貌的提出希望參加 面試 的請求。標(biāo)明與你聯(lián)系的最佳方式和預(yù)約 面試 的可能時間范圍。結(jié)尾亦可以禮節(jié)性的感謝對方花時間瀏覽簡歷并對你關(guān)注。
求職信寫作注意事項:
長度控制在400字以內(nèi),否則招聘人員沒有耐心閱讀。
通篇應(yīng)該沒有打字,拼寫,語法錯誤。
選擇質(zhì)量好的紙張,打印精美。
求職信是針對某一職位專門書寫得,不可隨意復(fù)印任意發(fā)送。
第四篇:DedeCMS專題功能使用詳細(xì)教程
愛雅逸整理http://2text(@me)'/}“ /> 這個代碼就是將你在添加專題時候設(shè)置的關(guān)鍵字以及專題說明添加進(jìn)頁面。
接下來最重要的就是專題節(jié)點部分的添加了,因為節(jié)點部分主要是列表,所以涉及到一個底層模板(innertext)的概念,底層模板在織夢的標(biāo)簽語法簡介中已經(jīng)有相關(guān)說明。(模板標(biāo)簽語法簡介:http://help.dedecms.com/v53/archives/templets/start/)因為我們從模板文件中可以找出單挑記錄循環(huán)的內(nèi)容為:
愛雅逸整理http://004km.cn
DEDECMS 專題詳解
接下來我們在節(jié)點部分加入節(jié)點標(biāo)簽{dede:field.notenoteid=?標(biāo)識?/},因為我們這里有2個節(jié)點名稱,標(biāo)識ID分別為: use(使用介紹)、exp(體驗說明),所以我們替換模板頁面中循環(huán)部分:
使用介紹 |
在我們上面設(shè)計的模板中,有2個節(jié)點,因為我們其中有一部分內(nèi)容已經(jīng)固化在模板中,所以如果直接發(fā)布內(nèi)容,會出現(xiàn)多出了節(jié)點標(biāo)題的情況,一個節(jié)點生成為頁面的時候,DedeCMS會讀取他的節(jié)點容器模板,所以我們需要對節(jié)點容器模板進(jìn)行一些修改,我們打開/templets/system/channel_spec_note.htm這是默認(rèn)的節(jié)點容器模板。
- ~notename~
-
- ~spec_arclist~
~spec_arclist~ 節(jié)點內(nèi)容列表,系統(tǒng)讀取底層模板文件,并解析成節(jié)點設(shè)置的內(nèi)容同這個標(biāo)記進(jìn)行替換
這個節(jié)點容器解析后的內(nèi)容再同{dede:field.notenoteid=?標(biāo)識?/}標(biāo)記進(jìn)行替換,完成模板的解析,所以我們在這里需要對其進(jìn)行一些修改,因為前面設(shè)計時候的節(jié)點名稱已經(jīng)固化在模板文件中,所以這里不涉及到這部分的內(nèi)容。我們只要將冗余部分的代碼去除,直接使之變?yōu)椋?~spec_arclist~
如果不想影響到其他模板文件的使用我們將其另存為: /templets/system/channel_spec_note_dedecms.htm 我們在添加專題時候可以獨立設(shè)置為:
愛雅逸整理http://004km.cn
DEDECMS 專題詳解
至此我們完成了專題頁面模板文件的制作,接下來做的只需要像上面所說的一樣,直接選取相應(yīng)的內(nèi)容添加節(jié)點發(fā)布即可。
織夢的專題功能還有更為強(qiáng)大的功能,需要我們織夢的用戶去用心體會,本文中已經(jīng)較為全面介紹了如何使用專題功能,文章中設(shè)計到的一些文件可以在附件中下載。
-------------專題中的文章是你從欄目中選擇出來的。如果要在欄目A中不顯示專題,那么需要在欄目A的列表htm模板中,將專題調(diào)用標(biāo)簽刪除,即可。織夢dedecms網(wǎng)站專題調(diào)用標(biāo)簽的使用方法:
dede織夢cms模板網(wǎng)站專題調(diào)用標(biāo)簽就是是用的arclist標(biāo)簽,只不過在里面用了一個spec的類型而已,下面模板團(tuán)給出三種調(diào)用方式,可以解決全部頁面的調(diào)用(首頁、一級、二級、單頁、內(nèi)容頁均有適合的)。
第五篇:小學(xué)四年級奧數(shù)教程—邏輯問題
邏輯問題(邏輯問題
(一)在日常生活中,有些問題常常要求我們主要通過分析和推理,而不是計算得出正確 的結(jié)論。這類判斷、推理問題,就叫做邏輯推理問題,簡稱邏輯問題。這類題目與我們學(xué)過 的數(shù)學(xué)題目有很大不同,題中往往沒有數(shù)字和圖形,也不用我們學(xué)過的數(shù)學(xué)計算方法,而是 根據(jù)已知條件,分析推理,得到答案。本講介紹利用列表法求解邏輯問題。例 1 小王、小張和小李一位是工人,一位是農(nóng)民,一位是教師,現(xiàn)在只知道:小李比教 師年齡大;小王與農(nóng)民不同歲;農(nóng)民比小張年齡小。問:誰是工人?誰是農(nóng)民?誰是教師? 分析與解: 分析與解:由題目條件可以知道:小李不是教師,小王不是農(nóng)民,小張不是農(nóng)民。由此 得到左下表。表格中打“√”表示肯定,打“×”表示否定。
因為左上表中,任一行、任一列只能有一個“√”,其余是“×”,所以小李是農(nóng) 民,于是得到右上表。因為農(nóng)民小李比小張年齡小,又小李比教師年齡大,所以小張比教師年齡大,即小 張不是教師。因此得到左下表,從而得到右下表,即小張是工人,小李是農(nóng)民,小王是教師。
圖片點擊可在新窗口打開查看
例 1 中采用列表法,使得各種關(guān)系更明確。為了講解清楚,例題中畫了幾個表,實際解 題時,不用畫這么多表,只在一個表中先后畫出各種關(guān)系即可。需要注意的是:①第一步應(yīng) 將題目條件給出的關(guān)系畫在表上,然后再依次將分析推理出的關(guān)系畫在表上; ②每行每列只 能有一個“√”,如果出現(xiàn)了一個“√”,它所在的行和列的其余格中都應(yīng)畫“×”。在下面的例題中,“√”和“×”的含義是很明顯的,不再單獨解釋。例 2 劉剛、馬輝、李強(qiáng)三個男孩各有一個妹妹,六個人進(jìn)行乒乓球混合雙打比賽。事先 規(guī)定:兄妹二人不許搭伴。第一盤:劉剛和小麗對李強(qiáng)和小英; 第二盤:李強(qiáng)和小紅對劉剛和馬輝的妹妹。問:三個男孩的妹妹分別是誰?
分析與解: 分析與解:因為兄妹二人不許搭伴,所以題目條件表明:劉剛與小麗、李強(qiáng)與小英、李 強(qiáng)與小紅都不是兄妹。由第二盤看出,小紅不是馬輝的妹妹。將這些關(guān)系畫在左下表中,由 左下表可得右下表。
劉剛與小紅、馬輝與小英、李強(qiáng)與小麗分別是兄妹。例 3 甲、乙、丙每人有兩個外號,人們有時以“數(shù)學(xué)博士”、“短跑健將”、“跳高冠 軍”、“小畫家”、“大作家”和“歌唱家”稱呼他們。此外:(1)數(shù)學(xué)博士夸跳高冠軍跳得高;(2)跳高冠軍和大作家常與甲一起去看電影;(3)短跑健將請小畫家畫賀年卡;(4)數(shù)學(xué)博士和小畫家很要好;(5)乙向大作家借
過書;(6)丙下象棋常贏乙和小畫家。你知道甲、乙、丙各有哪兩個外號嗎? 分析與解: 分析與解:由(2)知,甲不是跳高冠軍和大作家;由(5)知,乙不是大作家;由(6)知,丙、乙都不是小畫家。由此可得到下表:
因為甲是小畫家,所以由(3)(4)知甲不是短跑健將和數(shù)學(xué)博士,推知甲是歌唱 家。因為丙是大作家,所以由(2)知丙不是跳高冠軍,推知乙是跳高冠軍。因為乙是跳高 冠軍,所以由(1)知乙不是數(shù)學(xué)博士。將上面的結(jié)論依次填入上表,便得到下表:
所以,甲是小畫家和歌唱家,乙是短跑健將和跳高冠軍,丙是數(shù)學(xué)博士和大作家。例 4 張明、席輝和李剛在北京、上海和天津工作,他們的職業(yè)是工人、農(nóng)民和教師,已 知:(1)張明不在北京工作,席輝不在上海工作;(2)在北京工作的不是教師;(3)在上海工作的是工人;(4)席輝不是農(nóng)民。問:這三人各住哪里?各是什么職業(yè)? 分析與解: 分析與解:與前面的例題相比,這道題的關(guān)系要復(fù)雜一些,要求我們通過推理,弄清人 物、工作地點、職業(yè)三者之間的關(guān)系。三者的關(guān)系需要兩兩構(gòu)造三個表,即人物與地點,人 物與職業(yè),地點與職業(yè)三個表。我們先將題目條件中所給出的關(guān)系用下面的表來表示,由條件(1)得到表 1,由條 件(4)得到表 2,由條件(2)(3)得到表 3。因為各表中,每行每列只能有一個“√”,所以表(3)可填全為表(4)。
因為席輝不在上海工作,在上海工作的是工人,所以席輝不是工人,他又不是農(nóng)民,所以席輝是教師。再由表 4 知,教師住在天津,即席輝住在天津。至此,表 1 可填全為表 5。
對照表 5 和表 4,得到:張明住在上海是工人,席輝住在天津是教師,李剛住在北 京是農(nóng)民。練習(xí)26 1.甲、乙、丙分別是來自中國、日本和英國的小朋友。甲不會英文,乙不懂日語卻 與英國小朋友熱烈交談。問:甲、乙、丙分別是哪國的小朋友? 2.徐、王、陳、趙四位師傅分別是工廠的木工、車工、電工和鉗工,他們都是象棋 迷。
(1)電工只和車工下棋;(2)王、陳兩位師傅經(jīng)常與木工下棋;(3)徐師傅與電工下棋互有勝負(fù);(4)陳師傅比鉗工下得好。問:徐、王、陳、趙四位師傅各從事什么工種? 3.李波、顧鋒、劉英三位老師共同擔(dān)負(fù)六年級某班的語文、數(shù)學(xué)、政治、體育、音 樂和圖畫六門課的教學(xué),每人教兩門?,F(xiàn)知道:(1)顧鋒最年輕;(2)李波喜歡與體育老師、數(shù)學(xué)老師交談;(3)體育老師和圖畫老師都比政治老師年齡大;(4)顧鋒、音樂老師、語文老師經(jīng)常一起去游泳;(5)劉英與語文老師是鄰
鄰居。問:各人分別教哪兩門課程? 4.A,B,C,D 分別是中國、日本、美國和法國人。已知:(1)A 和中國人是醫(yī)生;(2)B 和法國人是教師;(3)C 和日本人職業(yè)不同;(4)D 不會看病。問:A,B,C,D 各是哪國人,5.小亮、小紅、小娟分別在一小、二小、三小讀書,各自愛好圍棋、體操、足球中 的一項,現(xiàn)知道:(1)小亮不在一?。唬?)小紅不在二??;(3)愛好足球的不在三小;(4)愛好圍棋的在一小,但不是小紅。問:小亮、小紅、小娟各在哪個學(xué)校讀書和各自的愛好是什么?