首先考虑使用更有意义的命名约定:我建议在表示列表的变量名称后附加一个“s”,并更系统地对它们进行编号(从 0 开始),并使用更具声明性和意义的谓词名称:
with_distinct_integers(E0, E) :-
term_variables(E0, Vs),
with_distinct_integers(E0, Vs, [0,1,2,3,4,5,6,7,8,9], E).
with_distinct_integers(E, [], [], E).
with_distinct_integers(E, [], _, E).
with_distinct_integers(E0, Vs0, Ns0, E) :-
select(Num, Ns0, Ns),
select(Var, Vs0, Vs),
Var is Num,
with_distinct_integers(E0, Vs, Ns, E).
专注于with_distinct_integers/4
现在。您会看到第一个子句被第二个包含,因此您可以省略第一个子句而不会丢失解决方案。该变量Var
仅用于将其与 统一Num
,因此您可以立即使用单个变量:
with_distinct_integers(E, [], _, E).
with_distinct_integers(E0, Vs0, Ns0, E) :-
select(Num, Ns0, Ns),
select(Num, Vs0, Vs),
with_distinct_integers(E0, Vs, Ns, E).
您仍然会发现使用此简化版本的意外重复解决方案,我将其作为一个简单的练习来找出导致此问题的原因:
?- with_distinct_integers(X-Y, [X,Y], [0,1], A).
..., A = 0-1 ;
..., A = 1-0 ;
..., A = 1-0 ;
..., A = 0-1 ;
false.
提示:只需对简化定义进行声明式推理即可。继续简化:当您已经拥有所需的一切(即它的变量)可用时,为什么还要绕过原始术语?考虑:
with_distinct_integers(E) :-
term_variables(E, Vs),
numlist(0, 9, Ns),
with_distinct_integers(Vs, Ns).
with_distinct_integers([], _).
with_distinct_integers([V|Vs], Ns0) :-
select(V, Ns0, Ns),
with_distinct_integers(Vs, Ns).
示例查询,计算所有解决方案:
?- findall(., with_distinct_integers([X-Y]), Ls), length(Ls, L).
Ls = ['.', '.', '.', '.', '.', '.', '.', '.', '.'|...],
L = 90.
旁边的惊喜:只有 90 个解决方案,而不是 99 个。
还可以考虑使用有限域约束,它们是整数上的关系,可以让您轻松制定此类任务:
:- use_module(library(clpfd)).
with_distinct_integers(E) :-
term_variables(E, Vs),
Vs ins 0..9,
all_different(Vs),
label(Vs).
示例查询:
?- with_distinct_integers(X-Y).
X = 0,
Y = 1 ;
X = 0,
Y = 2 ;
X = 0,
Y = 3 .