跳转到内容

编程语言导论/简单谓词

来自 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' 表达式的右侧,我们会得到同样的错误

下面我们有一个朴素最大公约数算法的实现,它使用了
=:=
华夏公益教科书