1. 程式人生 > >Prolog教程 13--定義操作符

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與其它語言的最大區別—程式與資料的高度統一。正是它的這種極其簡潔的表達形式,使得它被廣泛地應用於人工智慧領域。