削减确实是一个问题:例如尝试最一般的查询
?- count(Ls, L, C).
并看到它只产生一个解决方案,尽管显然应该有无限多,因为第一个参数可以是任意长度的列表。所以首先删除所有削减。另一个问题是(\=)/2
,它不是一个真正的关系:只有当它的论点有根据时它才是正确的。代替(\=)/2
,使用更通用的谓词dif/2
,它在 SWI-Prolog 以及其他系统中可用,并将其参数限制为不同的术语。然后,您的谓词将在各个方向上起作用。
编辑:我扩展了“全方位可用”这一点。考虑以下版本list_term_count/3
,它将列表与该列表中术语的出现次数相关联,除了使用clpfddif/2
约束:
list_term_count([], _, 0).
list_term_count([L|Ls], L, N) :-
list_term_count(Ls, L, N0),
N #= N0 + 1.
list_term_count([L|Ls], E, N) :-
dif(L, E),
list_term_count(Ls, E, N).
我们可以以最一般的方式使用它,即不指定所有参数,并获得正确的答案:
?- list_term_count(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 .
为了公平地列举所有解决方案,我们可以使用length/2
:
?- length(Ls, _), list_term_count(Ls, E, N).
Ls = [],
N = 0 ;
Ls = [E],
N = 1 ;
Ls = [_G167],
N = 0,
dif(_G167, E) .
请注意dif/2
作为剩余目标出现的约束,它约束列表的元素与E
when N
is不同0
。这就是我们如何表达无限的术语集,除了与 不同之外,不受任何其他方式的限制E
。
任何其他实例化模式也是可以接受的。例如:
?- list_term_count([a], E, N).
E = a,
N = 1 ;
N = 0,
dif(E, a).
或者例如:
?- list_term_count([X], a, N).
X = a,
N = 1 ;
N = 0,
dif(X, a).
这种普遍性是在程序中使用纯单调谓词的好处之一。使用纯目标还允许我们非常自由地重新排序它们。