说实话,我是 Prolog 新手,所以请原谅我的无知。
我有一个简单的谓词来计算列表中原子的出现次数,如下所示:
count(L, B, C) :-
L = [], C = 0, !;
L = [H|T], H \= B, count(T, B, C), !;
L = [H|T], H = B, count(T, B, C1), C is C1 + 1.
以下查询返回正确的结果:
?- count([a, c, g, t], a, C).
C = 1.
?- count([a, c, g, t], c, C).
C = 1.
?- count([a, c, g, t], g, C).
C = 1.
?- count([a, c, g, t], t, C).
C = 1.
但是,如果我尝试找到所有可能的解决方案,它只会给出一个。
?- count([a, c, g, t], X, C).
X = a,
C = 1.
如何让它给出所有解决方案?我认为它可能与剪切操作符有关,但删除它似乎也不起作用。
最佳答案
削减确实是一个问题:尝试例如最通用的查询
?- count(Ls, L, C).
并且看到它只产生一个解决方案,尽管显然应该有无限多个,因为第一个参数可以是任意长度的列表。所以首先删除所有切口。另一个问题是(\=)/2
,这不是一个真正的关系:只有当它的论点有根据时它才是合理的。使用更通用的谓词 dif/2
代替 (\=)/2
,该谓词在 SWI-Prolog 以及其他系统中可用,并将其参数限制为不同的术语。然后,您的谓词将在各个方向上发挥作用。
编辑:我扩展了“在各个方向上可用”这一点。考虑以下版本的 list_term_count/3
,它使用 clpfd 将列表与该列表中术语出现的次数相关联。除了 dif/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
约束,它作为剩余目标出现,并且在 N
时将列表的元素限制为与 E
不同是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).
这种通用性是在程序中使用纯单调谓词的好处之一。使用纯粹的目标还允许我们非常自由地重新排序它们。
关于prolog - 为什么SWI-Prolog只给出一种解决方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18806739/