程序可读性
即使在 Prolog 中也欢迎缩进,以使程序易于理解:
counter(T, [], X) :-
X is 0.
counter(T, [T|D], X1) :-
!,
counter(T, D, X),
X1 is X+1.
counter(T, [_|D], X1) :-
counter(T, D, X1).
变量命名
正确命名变量也是一个好习惯,我们可以在这里使用:
counter(Elem, [], Result) :-
Result is 0.
counter(Elem, [Elem|Tail], Result) :-
!,
counter(Elem, Tail, NewResult),
Result is NewResult + 1.
counter(Elem, [_|Tail], Result) :-
counter(Elem, Tail, Result).
单例变量
为单例变量指定一个特殊的名称也是一个好习惯(以_ 为前缀):
counter(_Elem, [], Result) :-
Result is 0.
counter(Elem, [Elem|Tail], Result) :-
!,
counter(Elem, Tail, NewResult),
Result is NewResult + 1.
counter(Elem, [_Head|Tail], Result) :-
counter(Elem, Tail, Result).
头部统一
您可以利用 Prolog 在子句头部使用统一来重写您的第一个子句这一事实:
counter(_Elem, [], Result) :-
Result is 0.
可以变成
counter(_Elem, [], 0).
那些只包含一个头部的子句也称为事实
尾递归
你必须改变的子句是中间子句:递归调用不在它的谓词末尾。可悲的是,它也会影响其他条款,让我们看看为什么。
为了获得尾递归,我们使用了一个称为累加器的习语:一个额外的参数,它将在递归期间保存中间结果。例如这里:
counter(Elem, List, Result) :-
counter(Elem, List, 0, Result).
counter(_Elem, [], Acc, Acc).
counter(Elem, [Elem|Tail], Acc, Result) :-
!,
NewAcc is Acc + 1,
counter(Elem, Tail, NewAcc, Result).
counter(Elem, [_Head|Tail], Acc, Result) :-
counter(Elem, Tail, Acc, Result).
如您所见,我们现在有一个谓词counter/3,它只调用counter/4,后者跟踪Acc 变量中的中间结果。
获得更通用的程序
您的程序中存在的一个问题是您使用了is/2。这并没有给你一个通用程序:你不能打电话给counter(X, [1, 2, 3, 4], R) 并得到答案。要纠正这一点,您可以使用约束编程:
:- use_module(library(clpfd)).
counter(Elem, List, Result) :-
counter(Elem, List, 0, Result).
counter(_Elem, [], Acc, Acc).
counter(Elem, [Elem|Tail], Acc, Result) :-
NewAcc #= Acc + 1,
counter(Elem, Tail, NewAcc, Result).
counter(Elem, [Head|Tail], Acc, Result) :-
Elem #\= Head,
counter(Elem, Tail, Acc, Result).
测试:
?- counter(X, [1, 2, 3, 4], R).
X = R, R = 1 ;
X = 2,
R = 1 ;
X = 3,
R = 1 ;
X = 4,
R = 1 ;
R = 0,
X in inf..0\/5..sup.