SWI-Prolog:检查多个列表中的元素成员资格

SWI-Prolog: Check element membership in multiple lists

所以我有一个成员函数,它告诉我什么时候 X 不在 L:

not_in(X, L) :- not(member(X,L)).

效果如愿:

83 ?- not_in(5,[3,4]).
true.

不过,我还有一个生成很多列表的函数:

84 ?- manyLists(_,L).
L = [5, 11] ;
L = [3, 4] ;
L = [16, 22] ;
false.

我想做的是检查 X 是否不在任何列表中:

manyLists(_,L), not_in(X,L).

但是,这个 returns:

85 ?- manyLists(_,L), not_in(32,L).
L = [5, 11] ;
L = [3, 4] ;
L = [16, 22] ;
false.

即它应该 return 正确,因为 32 不在任何这些列表中。

我做错了什么?

你没有做错任何事,但你可能没有正确解释你得到的解决方案。

首先,当您有一个成功不止一次的谓词时,这被称为(有点误导)非确定性行为:一个查询 returns 多个 解决方案.

假设你问,"which number is between 1 and 3?":

?- between(1, 3, X).
X = 1 ;
X = 2 ;
X = 3.

你得到三个答案。每个都有一个变量绑定X = 1X = 2X = 3.

?- between(1, 3, X), succ(X, Y).
X = 1, Y = 2 ;
X = 2, Y = 3 ;
X = 3, Y = 4.

现在每个解决方案都有两个变量绑定。

?- between(1, 3, X), X rem 2 =:= 0.
X = 2 ;
false.

如果您无法理解 Prolog 如何尝试为最后一个查询找到证明,您甚至可以查看实际的证明树:

?- trace(between/3), trace('=:='/2).
%         between/3: [call,redo,exit,fail]
%         (=:=)/2: [call,redo,exit,fail]
true.

[debug]  ?- between(1, 3, X), X rem 2 =:= 0.
 T Call: (8) between(1, 3, _G1248)
 T Exit: (8) between(1, 3, 1)
 T Call: (8) 1 rem 2=:=0
 T Fail: (8) 1 rem 2=:=0
 T Redo: (8) between(1, 3, _G1248)
 T Exit: (8) between(1, 3, 2)
 T Call: (8) 2 rem 2=:=0
 T Exit: (8) 2 rem 2=:=0
X = 2 ;
 T Redo: (8) between(1, 3, _G1248)
 T Exit: (8) between(1, 3, 3)
 T Call: (8) 3 rem 2=:=0
 T Fail: (8) 3 rem 2=:=0
false.

关于你的问题:你想证明对于你的列表的所有个可能的绑定,一个数字不在那个列表中。

您的原始查询,已简化:

?- ( L = [5,11] ; L = [3,4] ; L = [16,22] ), \+ member(32, L).
L = [5, 11] ;
L = [3, 4] ;
L = [16, 22].

将其包装在 SWI-Prolog 谓词中 forall/2:

?- forall( ( L = [5,11] ; L = [3,4] ; L = [16,22] ), \+ member(32, L) ).
true.

?- forall( ( L = [5,11] ; L = [3,4] ; L = [16,22] ), \+ member(3, L) ).
false.

如果你查看forall/2的帮助,你会看到forall(Cond, Action)具有语义\+ ( Cond, \+ Action ),即:"There is no condition for which action cannot be proved."

?- \+ ( ( L = [5,11] ; L = [3,4] ; L = [16,22] ), \+ \+ member(32, L) ).
true.