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。code

若是在一個表達式中存在多個相同優先級的操做符時會發生什麼?好比以前咱們說查詢,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)的表示方法。co

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。

相關文章
相關標籤/搜索