?- Es = [E1,E2], occurrences(E, Es, Fs).
Es = Fs, Fs = [E, E],
E1 = E2, E2 = E ;
Es = [E, E2],
E1 = E,
Fs = [E],
dif(E, E2) ;
Es = [E1, E],
E2 = E,
Fs = [E],
dif(E, E1) ;
Es = [E1, E2],
Fs = [],
dif(E, E1),
dif(E, E2).
虽然从声明的角度来看,该程序是完美无缺的,但在当前系统(如B、SICStus、SWI、YAP)上直接执行却效率低下。对于以下目标,每个列表元素都会留下一个选择点。
?- occurrences(a,[a,a,a,a,a],M). M = [a, a, a, a, a] ; false.可以通过使用足够大的
a
列表来观察到这一点,例如:
?- 24=I,N is 2^I,length(L,N), maplist(=(a),L). ERROR: Out of global stack必须调整
I
以便仍然可以表示列表;在SWI中,这意味着:
1mo 全局堆栈的I
必须足够小,以避免出现类似以下的资源错误:
2do 当地的堆栈的I
必须足够大才能引发资源错误:
?- 22=I,N是2的I次方,length(L,N), maplist(=(a),L), ( Length=ok ; occurrences(a,L,M) ). I = 22, N = 4194304, L = [a, a, a, a, a, a, a, a, a|...], Length = ok ; ERROR: Out of local stack
为了克服这个问题并仍然保留良好的声明性质,需要一些比较谓词。
应该如何定义这个比较谓词?
以下是这样一个可能的定义:
equality_reified(X, X, true). equality_reified(X, Y, false) :- dif(X, Y).
编辑:也许参数顺序应该与ISO内置compare/3
类似(链接仅链接到草案)。
它的高效实现将首先处理快速确定的情况:
equality_reified(X, Y, R) :- X == Y,!,R = true。 equality_reified(X, Y, R) :- ?=(X, Y),!,R = false。%语法不同 equality_reified(X, Y, R) :- X \= Y,!,R = false。%语义不同 equality_reified(X, X, true)。 equality_reified(X, Y, false) : dif(X,Y)。
编辑:我不确定在存在约束条件的情况下X \= Y
是否是适当的保护条件。 没有约束条件,?=(X, Y)
或X \= Y
是相同的。
例子
正如@user1638891所建议的那样,这里是一个如何使用这种原始方法的示例。mats的原始代码如下:
occurrences_mats(_, [], []).
occurrences_mats(X, [X|Ls], [X|Rest]) :-
occurrences_mats(X, Ls, Rest).
occurrences_mats(X, [L|Ls], Rest) :-
dif(X, L),
occurrences_mats(X, Ls, Rest).
这可以被重写为类似于:
occurrences(_, [], []).
occurrences(E, [X|Xs], Ys0) :-
reified_equality(Bool, E, X),
( Bool == true -> Ys0 = [X|Ys] ; Ys0 = Ys ),
% ( Bool = true, Ys0 = [X|Ys] ; Bool = true, Ys0 = Ys ),
occurrences(E, Xs, Ys).
reified_equality(R, X, Y) :- X == Y, !, R = true.
reified_equality(R, X, Y) :- ?=(X, Y), !, R = false.
reified_equality(true, X, X).
reified_equality(false, X, Y) :-
dif(X, Y).
请注意,SWI的第二参数索引仅在您输入类似于
occurrences(_,[],_)
的查询后才会被激活。此外,SWI需要本质上非单调的if-then-else,因为它不会对(;)/2
- 或者说是析取 - 进行索引。SICStus则会这样做,但只有第一个参数进行索引。因此,它会留下一个(1)选择点(在以[]
结尾的位置)。
occurrences/3
实现,除了我昨天提交的,都会在查询中第1、2个参数自由且第3个参数是带有不同ground成员的列表时进入无限循环,正确的行为当然是失败。 - migfilg