prolog - 为什么SWI-Prolog只给出一种解决方案?

标签 prolog prolog-dif logical-purity

说实话,我是 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,它使用 将列表与该列表中术语出现的次数相关联。除了 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/

相关文章:

prolog - setof/3 里面的 setof/3 不起作用,但为什么呢?

Prolog - 如果条件 1 AND 条件 2 执行 x

list - Prolog,尝试 append 2 个列表,但一直出错

prolog - 处理序言上下文无关语法

prolog - 关于 Prolog 中的平等和统一,我缺少什么?

list - 从列表中删除重复项,同时保留最右边的出现

prolog - Prolog 规则中目标(语句)的顺序

list - Prolog:列表和差异列表的图形表示

Prolog,关于如何形成更好的子句

prolog - Prolog:如何避免不削减成本地回溯?