我说实话,我是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.
如何让它提供所有解决方案?我虽然它可能与切割操作符有关,但删除它似乎也不起作用。
答案 0 :(得分:4)
剪切确实是一个问题:尝试例如最常见的查询
?- count(Ls, L, C).
并且看到它只产生一个解决方案,尽管显然应该有无限多,因为第一个参数可以是任意长度的列表。所以首先删除所有削减。另一个问题是(\=)/2
,这不是一个真正的关系:只有它的论据是基础的才是合理的。而不是(\=)/2
,使用更一般的谓词dif/2
,它可以在SWI-Prolog以及其他系统中使用,并将其参数约束为不同的术语。然后你的谓语将向各个方向发挥作用。
编辑:我展开了“可用于所有方向”的观点。考虑以下版本的list_term_count/3
,它将列表与该列表中术语的出现次数相关联,除了dif/2
之外还使用clpfd约束:
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
为N
时,该约束将列表的元素与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).
这种通用性是在程序中使用纯单调谓词的好处之一。使用纯粹的目标也可以让我们非常自由地重新排序它们。
答案 1 :(得分:2)
我认为你正在探索Prolog的“黑暗面”:聚合。
实际上,你的count / 3谓词可能是 - 图书馆(aggregate) -
count(L, B, C) :-
aggregate(count, member(B, L), C).
作为一种基于relational
数据模型的语言,我们可以期待SQL中常用的好东西,从较简单的那些开始:
select count( distinct B ) from L
如果您检查库实现(例如通过?- edit(aggregate/3).
),您将会理解将“一元一元”导向的Prolog 执行模型概括为所需的复杂性一个'面向'的人。
答案 2 :(得分:1)
意外的行为来自Prolog的统一,它总是试图成功。
将H \= B
视为not(H = B)
。如果B
未绑定,H = B
将始终成功,因为可以进行统一,因此not(...)
将始终失败。因此,在B
绑定到H
之后,递归仅发生在第三个分支中。
让我们假设H \= B
对于未绑定的B
会成功,并且会发生递归。现在,如果再次出现相同的元素H
,则此时它可能会绑定到B
,这将为每个值提供多个结果。例如,count([a,a],X,C)
将返回X = a, C = 1. X = a, C = 2.
。当然不是你想要的。
但是,如果我尝试找到所有可能的解决方案,它只会提供一个。
?- count([a, c, g, t], X, C). X = a, C = 1.
你会说“所有可能的解决方案”? X
为C
的{{1}}为无限值(Protip:try ?- X.
)。或者你的意思是列表中的所有值?
您的问题有一个非常简单的解决方案:只需确保在达到B
时绑定\=
。实现这一目标的最简单方法是简单地改变谓词的顺序,即将不等式移到最后。
% count(?List, ?Element, ?Count)
count([], _, 0).
count([E|R], E, C) :-
count(R, E, C0),
C is C0+1.
count([F|R], E, C) :-
count(R, E, C),
F \= E.
优化是留给读者的练习。
最后一点:在你真正了解它们之前不要使用剪切(!
),大多数时候你都不需要它们。
答案 3 :(得分:0)
这似乎有效。
base(a).
base(c).
base(g).
base(t).
count(L, B, C) :-
base(B),
(
L = [], C = 0;
L = [H|T], H = B, count(T, B, C1), C is C1 + 1;
L = [H|T], H \= B, count(T, B, C)
).
这是有道理的,因为Prolog没有任何其他方式可以知道我认为B的域是什么。我很惊讶它没有给我那个'论证没有充分实例化'错误的原始方式。此外,如果我包含剪切操作符,它不起作用,我现在也无法解释。