List 为什么Prolog会将一个变量与一个直接插入后失败的结果进行匹配?

List 为什么Prolog会将一个变量与一个直接插入后失败的结果进行匹配?,list,variables,prolog,match,prolog-dif,logical-purity,List,Variables,Prolog,Match,Prolog Dif,Logical Purity,我正在制作一个Prolog程序,用于查找一组列表的子集。此子集必须匹配某些特定条件,其中一个方面是子集的列表不能相同。让我困惑的是,当我试图为变量X找到匹配项时,如果我将它们插入到查询中而不是X,它会生成返回false的结果。例如: ?- containsSet(2, [[3],[7],[7]], X). X = [[3], [7]] ; X = [[3], [7]] ; X = [[7], [7]] ; false. ?- containsSet(2, [[3],[7],[7]], [[7]

我正在制作一个Prolog程序,用于查找一组列表的子集。此子集必须匹配某些特定条件,其中一个方面是子集的列表不能相同。让我困惑的是,当我试图为变量X找到匹配项时,如果我将它们插入到查询中而不是X,它会生成返回false的结果。例如:

?- containsSet(2, [[3],[7],[7]], X).
X = [[3], [7]] ;
X = [[3], [7]] ;
X = [[7], [7]] ;
false.

?- containsSet(2, [[3],[7],[7]], [[7],[7]]).
false.
not_member(X, Ls) :- maplist(dif(X), Ls).
如果直接插入时返回false,它怎么可能将X与[[7],[7]]匹配?


containsSet的思想是找到一个长度为N(在本例中为2)的列表子集,该子集在匹配位置没有匹配元素(即子集中没有两个列表具有相同的第一个元素或相同的第二个元素,等等)。在上面的示例中,[7]和[7]的第一个(也是唯一一个)元素匹配,因此返回false。

首先,祝贺您获得我在初学者提问中看到的最具陈述性和合理性的观察结果之一

一方面,合取在逻辑中是可交换的,这是最基本和最著名的性质之一。另一方面,许多Prolog初学者没有意识到使用像
(\+)/1
这样的非单调谓词几乎总是破坏这些基本不变量。您注意到这里发生了一些非常出乎意料的事情,您正确地期望Prolog提供更多正确的行为。幸运的是,在Prolog系统中,这些问题的声明式解决方案现在比以往任何时候都更广泛地传播

首先,考虑在你的程序中使用<代码>(1)/代码>:很快就会出现的一些问题:

?- X = d, \+ member(X, [a,b,c]). X = d. 因此,非单调谓词会导致各种杂质并违反声明语义。声明性地说,知道有解决方案,我们当然希望更一般的查询能够成功,但它失败了

在这种具体情况下,要指定一个术语不同于列表中的所有其他术语,请使用约束
dif/2
dif/2
可以在各个方向上工作,如果它的参数是变量,也可以得到正确的答案。例如:

?- containsSet(2, [[3],[7],[7]], X).
X = [[3], [7]] ;
X = [[3], [7]] ;
X = [[7], [7]] ;
false.

?- containsSet(2, [[3],[7],[7]], [[7],[7]]).
false.
not_member(X, Ls) :- maplist(dif(X), Ls).
这个定义保留了连接的可交换性,正如我们从纯逻辑关系中深切期望和事实上期望的那样:

?- not_member(X, [a,b,c]), X = d. X = d. ?- X = d, not_member(X, [a,b,c]). X = d. ?非成员(X[a,b,c]),X=d。 X=d。 ?-X=d,不是_成员(X[a,b,c])。 X=d。 由于
not_member/2
只使用纯谓词,您已经通过构造确保它只给出声明性正确的答案


对于以声明方式对代码进行推理(我对您的方法表示赞赏),我建议您使用纯单调的Prolog子集。有关更多信息,请参阅和。

观察效果非常好!这显然违反了连接的交换性,因此与我们从纯逻辑关系中所期望的背道而驰。很可能,您正在使用非单调和不纯的结构,如
(\+)/1
/0
或代码中的if-then-else。您应该通过使用像
dif/2
这样的约束来消除这些杂质,以表示这两个术语是不同的。这将使您的程序变得纯粹,并在更多方向上可用。见和。另外,
请使用更具可读性的名称
而不是名称。任何人都无法正确阅读
。啊!我在行中使用了几次
(\+)/1
set\u是兼容的(set):-\+(选择(X,set,R),\+列出兼容的(X,R))
。我会想办法重写这个。非常感谢。是的,我强烈建议使用纯谓词,如
dif/2
(\+)/1
版本将为您带来无尽的声明性问题。例如,考虑<代码>?-+选择(x,[a,b,c],REST),x= D.< /代码>,产生<代码> false <代码>,但是,如果我们仅仅通过(合意的)连接的交换性来交换这两个目标,我们将得到:<代码> x= d。code>(\+)/1如果其参数是有根据的,则它是正确的,但一般来说,您不能依赖此类非单调谓词来获得真正的一般性和声明性解决方案。你最好呆在纯单调的Prolog子集中,以保留这些好的属性。感谢这些例子!这真的很有启发性。我想我已经开始更好地理解Prolog是如何以这种方式工作的了