对我的代码进行哪些最小的更改可以使其保持逻辑纯洁性?

What minimal change to my code would make it preserve logical purity?

我在下面发布了代码 and 它在逻辑上并不纯粹,并且“如果您对代码进行最小的更改以使其保持逻辑纯度感兴趣,我建议发布一个关于这个的新问题。我很乐意回答它:)".

% minset_one(1 in D1, 1 in D2, D1, D2, D1Len, D2Len, T).
minset_one_(true,  false, D1, _,  _,     _,     D1).
minset_one_(false, true,  _,  D2, _,     _,     D2).
minset_one_(true,  true,  _,  D2, D1Len, D2Len, D2) :- D1Len >= D2Len.
minset_one_(true,  true,  D1, _,  D1Len, D2Len, D1) :- D1Len < D2Len.

minset_one(D1, D2, T) :-
    (member(1, D1) -> D1check = true ; D1check = false),
    (member(1, D2) -> D2check = true ; D2check = false),
    length(D1, D1Len),
    length(D2, D2Len),
    
    minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T).

例如

?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).

D1 = [1, Y, Z],
D2 = T, T = [1, V],
U = X, X = 1 ;

false

还有更多可能的解决方案。 member(1, D1) 不是通过 [1, Y, Z] 回溯,然后是 [X, 1, Z] 然后是 [X, Y, 1].

我认为是:

添加:

:- use_module(library(reif)).

... 并替换:

    %(member(1, D1) -> D1check = true ; D1check = false),
    %(member(1, D2) -> D2check = true ; D2check = false),
    memberd_t(1, D1, D1check),
    memberd_t(1, D2, D2check),

成员与memberd_t的区别举例:

?- member(X, [A, B, C]).
X = A ;
X = B ;
X = C.

?- memberd_t(X, [A, B, C], IsMember).
X = A,
IsMember = true ;
X = B,
IsMember = true,
dif(A,B) ;
X = C,
IsMember = true,
dif(A,C),
dif(B,C) ;
IsMember = false,
dif(A,X),
dif(B,X),
dif(C,X).

?- memberd_t(X, [A, B, C], IsMember), X = 5, A = 5, C = 5.
X = A, A = C, C = 5,
IsMember = true ;
false.

因此,memberd_t 本身就是在添加 dif/2 约束。为了稍微提高性能,它只循环一次列表。

memberd_t 的定义在例如https://github.com/meditans/reif/blob/master/prolog/reif.pl#L194 and https://www.swi-prolog.org/pack/file_details/reif/prolog/reif.pl?show=src

(->)/2(和朋友)的问题

考虑以下目标:

(member(1,D1) -> D1check = true ; D1check = false)

(->)/2 提交 member(1,D1) 的第一个答案——其他答案将被忽略。

(->)/2 的替代品——例如 (*->)/2(SWI、GNU)或 if/3(SICStus)——可以帮助我们吗?

No。这些 do 不会忽略使 member(1,D1) 成功的替代答案,但他们不认为 member(1,D1) 的逻辑否定可以 also 已成功。

回归基础:“If P then Q else R” ≡ “(P ∧ Q) ∨ (¬P ∧ R)”

所以让我们将 (If -> Then ; Else) 重写为 (If, Then ; Not_If, Else):

(member(1,D1), D1check = true ; non_member(1,D1), D1check = false)

我们应该如何实现non_member(X,Xs)——我们可以简单地写成\+ member(X,Xs)吗?

不!为了保持逻辑的纯洁性,我们最好不要建立在“否定是有限的失败”的基础上。

幸运的是,结合 maplist/2dif/2 可以完成这里的工作:

non_member(X,Xs) :-
   maplist(dif(X),Xs).

综合起来

所以这是我建议的最小更改:

minset_one_(true,  false, D1, _,  _,     _,     D1).
minset_one_(false, true,  _,  D2, _,     _,     D2).
minset_one_(true,  true,  _,  D2, D1Len, D2Len, D2) :- D1Len >= D2Len.
minset_one_(true,  true,  D1, _,  D1Len, D2Len, D1) :- D1Len < D2Len.

non_member(X,Xs) :- 
   maplist(dif(X),Xs).

minset_one(D1, D2, T) :-
   (member(1,D1), D1check = true ; non_member(1,D1), D1check = false),
   (member(1,D2), D2check = true ; non_member(1,D2), D2check = false),
   length(D1, D1Len),
   length(D2, D2Len),
   minset_one_(D1check, D2check, D1, D2, D1Len, D2Len, T).

运行 我们现在得到的示例查询:

?- D1 = [X,Y,Z], D2 = [U,V], minset_one(D1,D2,T).
   D1 = [1,Y,Z], X = U, U = 1, D2 = T, T = [1,V]
;  D1 = [1,Y,Z], X = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [1,Y,Z], X = 1, D2 = [U,V], dif(U,1), dif(V,1)
;  D1 = [X,1,Z], Y = U, U = 1, D2 = T, T = [1,V]
;  D1 = [X,1,Z], Y = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [X,1,Z], Y = 1, D2 = [U,V], dif(U,1), dif(V,1)
;  D1 = [X,Y,1], Z = U, U = 1, D2 = T, T = [1,V]
;  D1 = [X,Y,1], Z = V, V = 1, D2 = T, T = [U,1]
;  D1 = T, T = [X,Y,1], Z = 1, D2 = [U,V], dif(U,1), dif(V,1)
;  D1 = [X,Y,Z], D2 = T, T = [1,V], U = 1, dif(X,1), dif(Y,1), dif(Z,1)
;  D1 = [X,Y,Z], D2 = T, T = [U,1], V = 1, dif(X,1), dif(Y,1), dif(Z,1)
;  false.

更好。在我看来确实没有遗漏任何东西。