我有以下代码:
neighbor(C1, C2, [C1, C2|L]).
neighbor(C1, C2, [C2, C1|L]).
neighbor(C1, C2, [H|L]) :- neighbor(C1, C2, L).
not_neighbors(C5, C2, E, R) :-
not_neighbor(C5, C2, E).
not_neighbors(C5, C2, E, R) :-
not_neighbor(C5, C2, R).
/* THIS DON'T WORK */
not_neighbor(C5, C2, E) :-
\+ neighbor(C5, C2, E).
或者:
same_position(I, J, [I|U], [J|V]).
same_position(I, J, [M|U], [N|V]) :-
I \== M, % optimisation
J \== N, % optimisation
same_position(I, J, U, V).
/* THIS DON'T WORK */
not_under(C4, C1, R, E) :-
\+ same_position(C4, C1, R, E).
我知道问题在于否定,我希望得到same_position
的相反结果。
微米。 @CapelliC建议我使用dif/2
,但我不知道如何在我的具体例子中应用它。
答案 0 :(得分:3)
让我们首先从逻辑上思考列表中“邻居”的含义:在列表中A
和B
个邻居元素是什么情况?
回答:如果列表的格式为[...,X,Y,...]
且至少,则包含以下其中一项:
A = X
和 B = Y
A = Y
和 B = X
。按逻辑术语:(A = X
∧B = Y
)∨(A = Y
∧B = X
)。
我们想说明相反,这是否定公式:
((A = X
∧B = Y
)∨(A = Y
∧B = X
))≡
≡¬(A = X
∧B = Y
)∧¬(A = Y
∧B = X
)≡
≡(¬A = X
∨¬B = Y
)∧(¬A = Y
∨¬B = X
)≡
≡(A
≠X
∨B
≠Y
)∧(A
≠Y
∨{{1} }≠B
)
谁会想到De Morgan的法律有任何实际应用,对吧?
要在Prolog中声明X
≠X
,我们会使用强大的 Y
约束,正如@CapelliC已经建议的那样。 dif/2
为真 iff ,其参数为不同的条款。它是一个纯谓词,可以在各个方向正常运行。如果您的Prolog系统尚未提供,请确保让您的供应商知道您需要它!在此之前,如果需要,您可以使用iso_dif/2
近似
在Prolog中,上述因此变为:
( dif(A, X) ; dif(B, Y) ), ( dif(A, Y) ; dif(B, X) )
因为dif/2
表示连词,而(',')/2
表示 disjunction 。
所以我们有:
not_neighbours(_, _, []). not_neighbours(_, _, [_]). not_neighbours(A, B, [X,Y|Rest]) :- ( dif(A, X) ; dif(B, Y) ), ( dif(A, Y) ; dif(B, X) ), not_neighbours(A, B, [Y|Rest]).
一些测试用例让我们对谓词的正确性更有信心:
?- not_neighbours(a, b, [a,b]). false. ?- not_neighbours(A, B, [A,B]). false. ?- not_neighbours(A, B, [_,A,B|_]). false. ?- not_neighbours(A, B, [_,B,A|_]). false. ?- not_neighbours(a, b, [_,a,c,_]). true .
请注意,如果所有参数都是变量,这个定义也可以正常工作,我们称之为最常见的情况。
此解决方案的一个缺点是(;)/2
创建了许多替代方案,其中许多方案根本不重要。我们可以通过另一个代数等价来显着提高效率,让我们摆脱不必要的替代方案:
¬A∨B≡A→B
因此,在我们的案例中,我们可以写{((;)/2
∨¬A = X
)为B = Y
→A = X
≠B
。
我们可以使用强大的if_/3
元谓词在Prolog中表达暗示:
Y
因此我们可以声明性地将我们的解决方案写成:
not_neighbours(_, _, []). not_neighbours(_, _, [_]). not_neighbours(A, B, [X,Y|Rest]) :- impl(A=X, dif(B, Y)), impl(B=X, dif(A, Y)), not_neighbours(A, B, [Y|Rest]).
示例查询:
?- not_neighbours(a, b, [x,y]). true ; false.
更一般的情况:
?- not_neighbours(a, b, [X,Y]). X = a, dif(Y, b) ; X = b, dif(Y, a) ; dif(X, b), dif(X, a) ; false.
您可以使用此谓词来检查生成答案的和。尝试迭代深化以公平地列举所有答案:
impl(A, B) :- if_(A, B, true).
值得注意的是,纯粹的逻辑推理因此使我们成为一个普遍的和高效的Prolog程序。
?- length(Ls, _), not_neighbours(A, B, Ls).
起初可能对您来说不寻常,因为它出现在第一个Prolog系统中,然后被一些供应商忽略了一段时间。如今,dif/2
在越来越多的实现中变得可用(作为重要的内置谓词),允许您以声明方式声明两个术语不同。 dif/2
可以避免其不纯的替代方案在Prolog课程中造成的大量混淆。
答案 1 :(得分:1)
如果您想生成非邻居,\+
将无法按照定义 semidet 执行,并且永远不会绑定变量。你需要一些东西
构建答案。一种选择是生成所有对,然后使用\+ neighbor(...)
过滤非邻居。直接的建设性方法也不是那么难,尽管需要两个排序使代码变得复杂一点:
not_neighbor(C1, C2, List) :-
append(_, [C10,_|Postfix], List),
member(C20, Postfix),
swap(C1,C2, C10,C20).
swap(X,Y, X,Y).
swap(X,Y, Y,X).