Prolog教程 13--定義操作符
我們已經學習過了Prolog的資料結構,它的形式如下:
functor(arg1,arg2,…,argN).
這是Prolog的唯一的資料結構,但是Prolog允許這種資料結構有其它的表達方法(僅僅是表達方法不同)。這種表達方法有時候更加接近我們的習慣,正如列表的兩種表達法一樣。現在要介紹的是操作符語法。
以前曾經介紹了數學符號,在這一章我們將看到它和Prolog的資料結構是等價的,並且學習如何定義自己的操作符。
所有的數學操作符都是Prolog的基本符號,例如-/2、+/2、-/1。使用謂詞display/1可以看到它們的標準的語法結構。
?- display(2 + 2).
+(2,2)
?- display(3 4 + 6).
+((3,4),6)
?- display(3 (4 + 6)).
(3,+(4,6))
你可以把任何謂詞定義為操作符的形式,例如,如果我們把location/2定義為了操作符,那麼我們就可以用:
apple location kitchen.
來代替
location(apple, kitchen).
注意:這只是書寫形式上的不同,在Prolog進行模式匹配時它們都是一樣的。
操作符有三種形式:
中綴(infix):例如3+4
字首(prefix):例如-7
字尾(postfix):例如8 factorial
每個操作符有不同的優先權值,從1到1200。當某句中有多個操作符時,優先權高的將先被考慮。優先權值越小優先權越高。
使用內部謂詞op/3來定義操作符,它的三個引數分別是:優先權、結合性、操作符名稱。
結合性使用模板來定義,例如中綴操作符使用“xfx”來定義。“f”表示操作符的位置。
下面我們將重新編寫location/2謂詞,並改名為is_in/2。
is_in(apple, room(kitchen)).
使用op/3謂詞把is_in/2定義為操作符,優先權值為35。
?- op(35,xfx,is_in).
下面是我們的詢問。
?- apple is_in X.
X = room(kitchen)
?- X is_in room(kitchen).
X = apple
同樣可以使用操作符來定義事實。
banana is_in room(kitchen).
為了證明這兩種資料結構是等價,我們可以進行如下的比較:
?- is_in(banana, room(kitchen)) = banana is_in room(kitchen).
yes
使用display/1可以清楚地看到這一點。
?- display(banana is_in room(kitchen)).
is_in(banana, room(kitchen))
下面再把room/1定義為字首操作符。字首操作符的模板是fx。它的優先權應該比is_in的高。這裡取33。
?- op(33,fx,room).
?- room kitchen = room(kitchen).
yes
?- apple is_in X.
X = room kitchen
使用上面的兩個操作符,我們可以使用如下的方式定義事實。
pear is_in room kitchen.
?- is_in(pear, room(kitchen)) = pear is_in room kitchen.
yes
?- display(pear is_in room kitchen).
is_in(pear, room(kitchen))
注意如果操作符的優先權搞錯了,那就全部亂了套。例如:如果room/1的優先權低於is_in/2,那麼上面的結構就變成了下面這個樣子:
room(is_in(apple, kitchen))
不但如此,Prolog的聯合操作也將出現問題。所以一定要仔細考慮操作符的優先權。
最後我們來定義字尾操作符,使用模板xf。
?- op(33,xf,turned_on).
flashlight turned_on.
?- turned_on(flashlight) = flashlight turned_on.
yes
使用操作符可以是程式更容易閱讀。
在我們的命令驅動的“尋找Nani”遊戲中,為了使發出的命令更接近自然語言,可以使用操作符來定義。
goto(kitchen) -> goto kitchen.
turn_on(flashlight) -> turn_on flashlight.
take(apple) -> take apple.
雖然這還不是真正的自然語言,可是比起帶括號的來還是方便多了。
當操作符的優先權相同時,Prolog必須決定是從左到右還是從右到左地讀入操作符。這就是操作符的左右結合性。有些操作符沒有結合性,如果你把兩個這種操作符放到一起將產生錯誤。
下面是結合性的模板:
Infix:
xfx non-associative (沒有結合性)
xfy right to left
yfx left to right
Prefix
fx non-associative
fy left to right
Postfix:
xf non-associative
yf right to left
前面所定義的謂詞is_in/2沒有結合性,所以下面的句子是錯誤的。
key is_in desk is_in office.
為了表示這種巢狀關係,我們可以使用從右到左的結合性。
?- op(35,xfy,is_in).
yes
?- display(key is_in desk is_in office).
is_in(key, is_in(desk, office))
如果使用從左到右的結合性,我們的結果將不同。
?- op(35,yfx,is_in).
yes
?- display(key is_in desk is_in office).
is_in(is_in(key, desk), office)
但是使用括號可以改變這種結合性:
?- display(key is_in (desk is_in office)).
is_in(key, is_in(desk, office))
由許多內部謂詞都定義為了中綴操作符。因此我們可以使用“arg1 predicate arg2. ”來代替predicate(arg1,arg2) 。
我們所見過的數學符號就是如此,例如±/。但是一定要牢記這只是表達形式上的區別,因此3+4和7是不一樣的,它就是+(3,4)。
只有一些特殊的內部謂詞(例如is/2)進行真正的數學運算。is/2計算它右邊表示式的值,並讓左邊繫結為此值。它與聯合(=)謂詞是不同的,=只進行聯合而不進行計算。
?- X is 3 + 4.
X = 7
?- X = 3 + 4.
X = 3 + 4
?- 10 is 5 2.
yes
?- 10 = 5 2.
no
?- X is 3 4 + (6 / 2).
X = 15
?- X = 3 4 + (6 / 2).
X = 3 4 + (6 / 2)
?- X is +((3,4) , /(6,2)).
X = 15
?- 3 4 + (6 / 2) = +(: create,/(6,2)).
yes
只有當使用is/2來計算時,數學操作符才顯示出其不同之處,而一般情況下與其它的謂詞沒有任何區別。
?- X = 3 4 + likes(john, 6/2).
X = 3 4 + likes(john, 6/2).
?- X is 3 4 + likes(john, 6/2).
error
我們已經知道Prolog的程式是由一系列的子句構成的。其實這些子句也是使用操作符書寫的Prolog的資料結構。這裡的操作符是":-",它是中綴操作符,有兩個引數。
:-(Head, Body).
Body也是由操作符書寫的資料結構。這裡的操作符為",",它表示並且的意思,所以Body的形式如下:
,(goal1, ,(goal2,goal3))
好像看不明白,操作符",“與分隔符”,“無法區別,所以我們就是用”&“來代替操作符”,",於是上面的形式就變成了下面這個樣子了。
&(goal1, &(goal2, & goal3))
下面的兩種形式表達的意思是相同的。
head :- goal1 & goal2 & goal3.
:-(head, &(goal1, &(goal2, & goal3))).
實際上是下面的形式:
head :- goal1 , goal2 , goal3.
:-(head, ,(goal1, ,(goal2, , goal3))).
數學操作符不但可以用來計算,還有許多其它的用途。例如write/1,只能有一個引數,當我們想同時顯示兩個變數的值時, 就可以使用下面的方法。
?- X = one, Y = two, write(X-Y).
one - two
因為X-Y實際上是一個數據結構,所以它相對於write來說就只是一個引數。
當然其它的數學操作符也能完成相同的功能,例如/。在有些Prolog的版本中乾脆引入了“:”這個操作符來專門完成這種任務,有了它我們可以很方便的書寫複雜的資料結構了。
object(apple, size:small, color:red, weight:1).
?- object(X, size:small, color:C, weight:W).
X = apple
C = red
W = 1
這裡我們使用size:small,代替了原來的size(small),實際上“:”是中綴操作符,它的原始表達形式是:(size,small)。
從這一章所介紹的內容我們可以發現Prolog的程式實際上也是一種資料結構,只不過是使用專門的操作符連線起來的。那麼到現在為止,我們所學習過的所有Prolog內容:事實、規則、結構、列表等的實質都是一樣的,這也正是Prolog與其它語言的最大區別—程式與資料的高度統一。正是它的這種極其簡潔的表達形式,使得它被廣泛地應用於人工智慧領域。