Learn Prolog Now 翻譯 - 第九章 - 語句深究 - 第四節, 操作符


內容提要

  • 操作符的屬性
  • 自定義操作符


正如我們之前看到的,在一些特定的情況下(比如,當進行數字計算時),Prolog允許我們書寫比內部表示更加友好的操作符語法。事實上,如我們將要學習到的,Prolog甚至允許我們自定義操作符。這本章中,我們將會學習操作符的屬性,及其如何自定義操作符。


操作符的屬性

首先從數字運算的例子開始。Prolog會在內部使用這樣的表達式:is(11, +(2, 、*(3,3))),但是我們可以自由地在編程中將函子*和+寫在參數之間,從而構成更加友好的表達式:11 is 2 + 3*3。函子能夠寫在參數之間被稱為中綴操作符。Prolog中其他一些中綴操作符的例子是::-, -->, =, =.., ==等等。除了中綴操作符之外,還有前綴操作符(寫在參數之前)和后綴操作符(寫在參數之后)。比如,?-是一個前綴操作符,還有就是單獨的負號-,代表負數(比如, 1 is 3+ -2)。后綴表達式的一個例子就是在C語句中的++,可以用於變量的自增。

當我們學習Prolog的數字運算時,我們已經了解到Prolog可以消除數字運算表達式的歧義。所以當我們寫 2 + 3*3的時候,Prolog知道其含義是:2 + (3*3),而不是 (2 + 3) * 3。但是Prolog是如何知道的?因為每一個操作符都有一定的優先級。+的優先級比*的優先級別更高,所以+能夠成為表達式 2 + 3*3的主函子。(這里注意理解Prolog優先級高的含義和我們平時的理解不一致,優先級高在Prolog中是指越外層的函子,比如內部表達式為:+(2, *(3, 3)))。類似地,is的優先級比+的優先級高,所以 11 is 2 + 3*3 會被轉換為內部的表達式:is(11, +(2, *(3, 3)))。在Prolog中,優先級使用從0到1200的數字表示;最大的數字,代表最高的優先級。給出一些例子,=的優先級是700,+的優先級是500,*的優先級是400。

如果在一個表達式中存在多個相同優先級的操作符時會發生什么?比如之前我們說查詢,2 =:= 3 == =:=(2,3)會使得Prolog報錯。Prolog不知道如何解析表達式,是 =:=(2, ==(3, =:=(2,3))),還是 ==(=:=(2,3), =:=(2,3))?原因是因為== 和 =:=有相同的優先級,而Prolog不能決定正確的括號方式。在這種情況下,顯式的括號是必須要程序中提供的。



下面的查詢會如何進行?

?- X is 2 + 3 + 4.

Prolog會困惑嗎?完全不會:它工作的很愉快並且得出的正確的答案 X = 9。但是內部采用了哪種括號的方式,是 is(X, +(2, +(3,4))),還是 is(X, +(+(2,3), 4))?如下面的查詢所示,Prolog選擇的是第二年方式:

?- 2 + 3 + 4 = +(2, +(3, 4)).
false

?- 2 + 3 + 4 = +(+(2, 3), 4).
true

這里Prolog會使用+的結合性來消除歧義:+是左結合性的,意味着+右邊的表達式的優先級並且小於+本身的優先級,同時左邊的表達式的優先級必須等於+本身的優先級。一個表達式的優先級簡單地認為和其主操作符的優先級一致,或者當被括號括起來時為0. 3 + 4這個表達式的主操作符是+,所以將 2 + 3 + 4 轉換為 +(2, +(3, 4))意味着第一個+右邊的表達式的優先級和+本身一致,這是不正確的。它必須要更低才行。

操作符==,=:=被定義為沒有結合性,這意味着操作符兩邊的參數必須要有更低的優先級。所以 2 =:= 3 == =:=(2, 3)是一個錯誤的表達式,因為無論如何加括號,都會有歧義:2 =:= 3有和=相同的優先級,同時 3 == =:=(2,3)和=:=有相同的優先級。

操作符的類型(中綴,前綴和后綴),它們的優先級,和它們的結合性是Prolog中關於操作符必須了解的知識,這樣我們才能夠寫出符合用戶使用習慣,同時滿足Prolog內部表達方式的代碼。


自定義操作符

除了為特定的一些函子提供了友好的操作符語法外,Prolog也允許自定義操作符。比如,你能夠定義一個后綴操作符,is_dead,Prolog允許你在知識庫中寫出 zed is_dead 來替代標准的 is_dead(zed)的表示方法。

Prolog的自定義操作符看上去如下:

:- op(Precedence, Type, Name).

正如之前提及的,優先級是一個從0到1200的數字,數字越大,優先級越高(再次強調,這里的優先級高,是指函子使用在越外層,和我們平時理解的優先級高先運算和調用是相反的)。Type是一個原子,表示操作符的類型和結合性。比如+的這個原子是yfx,含義是說+是一個中綴操作符,f代表操作符,x和y代表參數。更進一步地說,x的優先級低於+的優先級,y的優先級低於或者等於+的優先級。這里有一些可能的type:

infix xfx, xfy, yfx
prefix fx, fy
suffix xf, yf

所以我們自定義的操作符is_dead代碼如下:

?- op(500, xf, is_dead).

這里有一些內置操作符的定義。可以看到相同屬性的操作符定義在一個子句中,通過最后一個參數給出名字的列表:

:- op(1200, xfx, [ :-, -->]).
:- op(1200,  fx, [ :-, ?-]).
:- op(1100, xfy, [;]).
:- op(1000, xfx, [ ',' ]).
:- op( 700, xfx, [ =, is, =.., ==, \==, =:=, =\=, <, >, =<, >=]).
:- op( 500, yfx, [ +, -]).
:- op( 500,  fx, [ +, -]).
:- op( 300, xfx, [ mod ]).
:- op( 200, xfy, [ ^ ]).

最后需要明確的一點是,自定義操作符不會實現操作符的功能,而只是定義如果使用操作符。即,一個自定義的操作符不會包括查詢在什么情況下為真的運算,它僅僅是Prolog在語法層面的擴充。所以如果操作符is_dead想上面那樣定義,並且你直接查詢:zed is_dead,Prolog不會報語法有錯誤,但是同時會證明的目標是:is_dead(zed),這是Prolog內部的標准表示方法。所以這就是自定義操作符做的一切——只是將友好的語法轉為Prolog真正內部的表示法。所以,Prolog將會如何回答這個查詢呢?答案是false,因為Prolog試圖證明:is_dead(zed),但是在知識庫中找不到能夠匹配的目標。但是,如果我們擴展知識庫如下:

:- op(500, xf, is_dead).

kill(marsellus, zed).
is_dead(X) :- kill(_, X).

這時Prolog會根據知識庫的事實和規則,回答true。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com