如果直接插入,为什么 Prolog 会将变量匹配到失败的结果?
Why would Prolog match a variable to a result that fails if plugged in directly?
我正在制作一个 Prolog 程序,用于查找一组列表的子集。该子集必须符合某些特定条件,其中一个方面是子集的列表不能相同。令我困惑的是,当我尝试为变量 X 找到匹配项时,如果我将它们插入查询而不是 X,它会生成 return 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.
它怎么可能将 X 匹配到 [[7], [7]] 如果直接插入时它 return 是错误的?
containsSet 的思想是找到长度为 N(在本例中为 2)的列表子集,该子集在匹配位置没有匹配元素(即子集中没有两个列表具有相同的第一个元素,或相同的第二个元素等)。在上面的示例中,[7] 和 [7] 的第一个(也是唯一一个)元素匹配,因此 return 为 false。
首先,恭喜您提出了我在初学者提问中看到的最明确、最合理的观察结果之一!
一方面,conjunction在逻辑上是可交换的,这是最基本和众所周知的性质之一。另一方面,许多 Prolog 初学者没有意识到使用一个非单调谓词如 (\+)/1
几乎总是 破坏 这样的基本不变量。您注意到这里发生了一些非常出乎意料的事情,并且您期望 Prolog 有更正确的行为是正确的。幸运的是,针对此类问题的声明式解决方案现在比以往任何时候都在 Prolog 系统中得到更广泛的传播。
首先,考虑一下如果在程序中使用 (\+)/1
很快就会出现的一些问题:
?- X = d, \+ member(X, [a,b,c]).
X = d.
然而,如果我们简单地通过合取的交换性来交换目标,我们会得到不同的答案:
?- \+ member(X, [a,b,c]), X = d.
false.
这表明 (\+)/1
不是 单调的:它可能导致 更一般的 查询失败,尽管 更具体查询产生解决方案:
?- \+ member(X, [a,b,c]).
false.
?- \+ member(d, [a,b,c]).
true.
因此,非单调谓词会导致各种不纯并违反声明语义。明确地说,知道有解决方案,我们当然期望更一般的查询 成功 ,但它 失败 .
在此具体中,要指定一个术语与列表中的所有其他术语不同,请使用约束 dif/2
. dif/2
适用于所有方向,如果其参数是变量,也会产生正确的答案。例如:
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.
由于 not_member/2
仅使用纯谓词,您已经确保 - 已经通过构造 - 它仅给出声明性的正确答案。
为了对您的代码进行声明式推理(我对您的方法表示赞赏),我建议您留在 Prolog 的纯单调子集中。有关详细信息,请参阅 logical-purity and prolog-dif。
我正在制作一个 Prolog 程序,用于查找一组列表的子集。该子集必须符合某些特定条件,其中一个方面是子集的列表不能相同。令我困惑的是,当我尝试为变量 X 找到匹配项时,如果我将它们插入查询而不是 X,它会生成 return 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.
它怎么可能将 X 匹配到 [[7], [7]] 如果直接插入时它 return 是错误的?
containsSet 的思想是找到长度为 N(在本例中为 2)的列表子集,该子集在匹配位置没有匹配元素(即子集中没有两个列表具有相同的第一个元素,或相同的第二个元素等)。在上面的示例中,[7] 和 [7] 的第一个(也是唯一一个)元素匹配,因此 return 为 false。
首先,恭喜您提出了我在初学者提问中看到的最明确、最合理的观察结果之一!
一方面,conjunction在逻辑上是可交换的,这是最基本和众所周知的性质之一。另一方面,许多 Prolog 初学者没有意识到使用一个非单调谓词如 (\+)/1
几乎总是 破坏 这样的基本不变量。您注意到这里发生了一些非常出乎意料的事情,并且您期望 Prolog 有更正确的行为是正确的。幸运的是,针对此类问题的声明式解决方案现在比以往任何时候都在 Prolog 系统中得到更广泛的传播。
首先,考虑一下如果在程序中使用 (\+)/1
很快就会出现的一些问题:
?- X = d, \+ member(X, [a,b,c]). X = d.
然而,如果我们简单地通过合取的交换性来交换目标,我们会得到不同的答案:
?- \+ member(X, [a,b,c]), X = d. false.
这表明 (\+)/1
不是 单调的:它可能导致 更一般的 查询失败,尽管 更具体查询产生解决方案:
?- \+ member(X, [a,b,c]). false. ?- \+ member(d, [a,b,c]). true.
因此,非单调谓词会导致各种不纯并违反声明语义。明确地说,知道有解决方案,我们当然期望更一般的查询 成功 ,但它 失败 .
在此具体中,要指定一个术语与列表中的所有其他术语不同,请使用约束 dif/2
. dif/2
适用于所有方向,如果其参数是变量,也会产生正确的答案。例如:
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.
由于 not_member/2
仅使用纯谓词,您已经确保 - 已经通过构造 - 它仅给出声明性的正确答案。
为了对您的代码进行声明式推理(我对您的方法表示赞赏),我建议您留在 Prolog 的纯单调子集中。有关详细信息,请参阅 logical-purity and prolog-dif。