跳轉到內容

程式語言導論/簡單謂詞

來自 Wikibooks,開放的書籍,為開放的世界

Prolog 是一種圍繞符號概念構建的程式語言:原子是符號。因此,除非我們將某些特定的語義賦予像 '1'、'2' 和 '+' 這樣的符號,否則它們將與 'a'、'@hi' 和 'dcc024' 一樣被對待。為了讓這種符號概念更清晰,讓我們考慮下面的謂詞,它旨在計算列表的長度

myLength([], 0).
myLength([_|Tail], Len) :- myLength(Tail, TailLen), Len = TailLen + 1.

即使這個謂詞有一個非常明顯的實現,如果我們執行它,那麼,除非我們熟悉 Prolog,否則我們應該會得到一些意想不到的結果。例如,在 swi-prolog 中,我們得到以下結果

?- myLength([a, b, c], X).
X = 0+1+1+1.

這並不完全是我們想要的,對吧?關鍵是 '0'、'+' 和 '1' 只是為了統一目的的原子。因此,'0 + 1' 與 'a + b' 沒有更多意義,就像我們在 swi-prolog 中檢查的那樣

?- X = a + b.
X = a+b.

?- X = 0 + 1.
X = 0+1.

當然,Prolog 提供了一些方法來理解 '1' 代表的數字。類似地,該語言提供了一些方法來解釋 '+' 作為算術加法。其中一種方法是使用關鍵字is. 像這樣的項X is 1 + 1具有以下語義:等式的右側,在本例中為 '1 + 1',使用傳統的加法解釋進行計算,然後結果與變數 'X' 統一。有了這個新的關鍵字,我們可以用以下方式重寫長度謂詞

isLength([], 0).
isLength([_|Tail], Len) :- isLength(Tail, TailLen), Len is TailLen + 1.

請注意,這個謂詞不是一個尾遞迴函式。為了使其成為尾遞迴函式,我們可以按照上一章中看到的技術,向它新增第三個引數

tailLength([], Acc, Acc).
tailLength([_|Tail], Acc, Len) :- NewAcc is Acc + 1, tailLength(Tail, NewAcc, Len).

使用關鍵字isis我們可以編寫一些在列表上工作的簡單謂詞。這些謂詞依賴於我們在研究函數語言程式設計時看到的相同原理。我們有一個基本情況,它決定一旦我們到達空列表,該做什麼,並且我們有一個歸納步驟,它處理非空列表。例如,下面我們有一個謂詞的實現sum

sum([],0).
sum([Head|Tail],X) :- sum(Tail,TailSum), X is Head + TailSum.

sumAcc([], Acc, Acc).
sumAcc([H|T], Acc, X) :- Aux is Acc + H, sumAcc(T, Aux, X).
sumTC(L, S) :- sumAcc(L, 0, S).

,它將列表中的元素加起來。為了完整起見,我們也展示了它的尾遞迴版本下面我們有一個永恆存在的謂詞的實現factorial謂詞。請注意,在這個例子中,我們檢查了一個表示式是否編碼了一個整數。Prolog 是一種動態型別程式語言。因此,原子只是原子。原則上,它們不被視為任何特殊型別。這意味著我們可以有數字和其他符號的列表,例如[1, +, a]

fact(0, 1).
fact(1, 1).
fact(N, F) :- integer(N), N > 1, N1 is N - 1, fact(N1, F1), F is F1 * N.

,例如。is除了運算子=:=isis之外,Prolog 還提供了運算子=:=來計算算術表示式。這個運算子的語義略微不同於is的語義。項X =:= Y強制計算兩者,

?- 1 + 2 is 4 - 1.
false.

?- 1 + 2 =:= 4 - 1.
true.

?- X is 4 - 1.
X = 3.

?- X =:= 4 - 1.
ERROR: =:=/2: Arguments are not sufficiently instantiated

X

?- 1 is X.
ERROR: is/2: Arguments are not sufficiently instantiated

=:=Y

gcd(X, Y, Z) :- X =:= Y, Z is X.
gcd(X, Y, Denom) :- X > Y, NewY is X - Y, gcd(Y, NewY, Denom).
gcd(X, Y, Denom) :- X < Y, gcd(Y, X, Denom).

. 然後將此計算的結果進行統一。例如

gcd(N1, N2, GCD) :- N1 < N2, euclid(N2, N1, GCD).
gcd(N1, N2, GCD) :- N2 > 0, N1 > N2, euclid(N1, N2, GCD).

euclid(N, 0, N).
euclid(N1, N2, GCD) :- N2 > 0, Q is N1 // N2, R is N1 - (N2 * Q), euclid(N2, R, GCD).

在最後一個查詢中,我們得到一個錯誤,因為變數 'X' 沒有定義。我們無法計算未定義變數的算術值。如果該變數是 'is' 表示式的右側,我們會得到同樣的錯誤

下面我們有一個樸素最大公約數演算法的實現,它使用了
=:=
華夏公益教科書