Prolog:模拟析取事实

时间:2015-09-08 08:03:09

标签: prolog logic

我有一个我想解决的逻辑问题,所以我想,“我知道,我会尝试Prolog!”

不幸的是,我几乎立即遇到了一堵砖墙。其中一个假设是一个分离的事实; A,B或C是真的(或多于一个),但我不知道哪个。我已经知道这是something Prolog does not support

有很多文档可以解决这个问题,但大多数文档似乎立即涉及更复杂的概念并解决了更高级的问题。我正在寻找的是一种模拟定义上述事实的孤立方式(由于Prolog的限制,不可能直接定义它)。

我怎么能解决这个问题?我能以某种方式将它包装在规则中吗?

编辑:我意识到我还不是很清楚。鉴于我对Prolog不熟悉,我不想在尝试传达问题时陷入语法错误,而是采用自然语言。我想这没有用,所以无论如何我都会在伪Prolog中试一试。

直观地说,我想要做的就是这样,宣布foo(a)foo(b)foo(c)成立,但我不知道:

foo(a); foo(b); foo(c).

然后我希望得到以下结果:

?- foo(a); foo(b); foo(c).
true

不幸的是,我试图声明的事实(即foo(x)至少保留一个x \in {a, b, c})不能这样定义。具体而言,它会产生No permission to modify static procedure '(;)/2'

旁注:在宣布析取事实之后,从逻辑的角度来看,?- foo(a).的结果对我来说有点不清楚;它显然不是true,但false也没有涵盖它 - 在这种情况下,Prolog根本没有足够的信息来回答该查询。

编辑2:以下是更多上下文,使其更像是一个真实场景,因为我可能过度简化并丢失了翻译细节。

假设涉及三个人。爱丽丝,鲍勃和查理。 Bob拥有两张牌{1, 2, 3, 4}。爱丽丝问他问题,回答这个问题,他向她展示了查理没看到的一张卡片,或者没有显示卡片。如果有更多卡适用,Bob只会显示其中一张卡。查理的任务是了解鲍勃持有的牌。正如人们所料,查理是一个自动化系统。

爱丽丝问鲍勃“你有1还是2?”,鲍勃向艾丽斯展示了一张卡片。查理现在得知鲍勃拥有1或2。

爱丽丝然后询问“你有一个2或3”,鲍勃没有卡片显示。显然,鲍勃有一个1,他之前向爱丽丝展示过。基于这两个事实,查理现在应该能够得出这个。

我想要建模的是Bob拥有1或2(own(Bob, 1) \/ own(Bob, 2))的知识,而Bob不拥有2或3(not (own(Bob, 2) \/ own(Bob, 3)))。查询Bob是否拥有1应该是true;查理可以得出这个。

3 个答案:

答案 0 :(得分:8)

您问题的直接回答

如果您可以使用有限域上的约束逻辑编程对问题进行建模,则可以使用#\实现“异或”,如下所示:

  

在三个变量X,Y,Z中,恰好一个可以在域1..3中。

D = 1..3, X in D #\ Y in D #\ Z in D

要概括这一点,你可以写:

disj(D, V, V in D #\ Rest, Rest).

vars_domain_disj([V|Vs], D, Disj) :-
    foldl(disj(D), Vs, Disj, V in D #\ Disj).

并将其用作:

?- vars_domain_disj([X,Y,Z], 2 \/ 4 \/ 42, D).
D = (Y in 2\/4\/42#\ (Z in 2\/4\/42#\ (X in 2\/4\/42#\D))).

如果您不使用CLP(FD),例如您无法在问题和整数之间找到一个很好的映射,那么您可以执行其他操作。假设您的变量位于列表List中,其中任何一个,但只有一个,可以是foo,其余的不能是foo,您可以说:

?- select(foo, [A,B,C], Rest), maplist(dif(foo), Rest).
A = foo,
Rest = [B, C],
dif(B, foo),
dif(C, foo) ;
B = foo,
Rest = [A, C],
dif(A, foo),
dif(C, foo) ;
C = foo,
Rest = [A, B],
dif(A, foo),
dif(B, foo) ;
false.

查询显示:在列表[A,B,C]中,其中一个变量可以是foo,其余变量必须与foo不同。您可以看到该查询的三种可能解决方案。

原始答案

可悲的是,经常声称Prolog不支持这样或那样的事情;通常,这不是真的。

目前你的问题并不完全清楚,但是你说这意味着,通过这个程序:

foo(a).
foo(b).
foo(c).

您可以获得以下问题的答案:

?- foo(X).
X = a ;
X = b ;
X = c.

你可能解释为:

  

foo(a)为真, foo(b)为真, foo(c)为真。

但是,如果我理解你的问题,你需要一个规则,例如:

  

foo(a)foo(b)foo(c)中只有一个可以为真。

但是,根据上下文,它,您的程序的其余部分和您的查询,原始解决方案可能意味着!

但你真的需要在你的问题中更加具体,因为解决方案将取决于它。

编辑问题后编辑

这是一个特殊问题的解决方案,使用有限域上的约束编程,使用了很好的library(clpfd) by Markus Triska,可以在SWI-Prolog中找到。

以下是完整代码:

:- use_module(library(clpfd)).

cards(Domain, Holds, QAs) :-
    all_distinct(Holds),
    Holds ins Domain,
    maplist(qa_constraint(Holds), QAs).

qa_constraint(Vs, D-no) :-
    maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
    foldl(disj(D), Vs, Disj, V in D #\ Disj).

not_in(D, V) :- #\ V in D.

disj(D, V, V in D #\ Rest, Rest).

两个示例查询:

?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 1.
X = 1,
Y = 4 ;
false.
  

如果这套牌是{1,2,3,4},而鲍勃持有两张牌,当爱丽丝问“你有1还是2”时,他说“是”,当她问“做”时你有2或3“他说没有,那么:查理能否知道Bob是否持有1?

答案是:

  

是的,如果Bob拿着1,另一张牌是4;没有其他可能的解决方案。

或者:

?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 3.
false.
  

如上所述,Charlie能否知道Bob是否持有3?

     

查理肯定知道鲍勃没有拿三个人!

这一切意味着什么?

:- use_module(library(clpfd)).

使图书馆可用。

cards(Domain, Holds, QAs) :-
    all_distinct(Holds),
    Holds ins Domain,
    maplist(qa_constraint(Holds), QAs).

这定义了我们可以从顶层查询的规则。第一个参数必须是一个有效的域:在您的情况下,它将1..4表明卡在集合{1,2,3,4}中。第二个参数是变量列表,每个变量代表Bob持有的一张卡片。最后一个是“问题”和“答案”列表,每个格式都是Domain-Answer格式,因此1\/2-yes表示“对于问题,您是否持有1或2,答案是'是' ”

然后,我们说Bob持有的所有卡片都是不同的,并且每个卡片都是其中一个,然后我们将每个问答配对映射到卡片上。

qa_constraint(Vs, D-no) :-
    maplist(not_in(D), Vs).
qa_constraint([V|Vs], D-yes) :-
    foldl(disj(D), Vs, Disj, V in D #\ Disj).

“不”答案很简单:只需说明对于Bob持有的每张卡片,在提供的域中#\ V in D

not_in(D, V) :- #\ V in D.

“是”的回答意味着我们需要Bob持有的独家或全部牌; 2\/3-yes应该会导致“第一张卡片是2张或3张,或者第二张卡片是2张或3张,但不是两张!”

disj(D, V, V in D #\ Rest, Rest).

要了解最后一个,请尝试:

?- foldl(disj(2\/3), [A,B], Rest, C in 2\/3 #\ Rest).
Rest = (A in 2\/3#\ (B in 2\/3#\ (C in 2\/3#\Rest))).

答案 1 :(得分:4)

vanilla Prolog中的生成和测试解决方案:

 card(1). card(2). card(3). card(4).

 owns(bob, oneof, [1,2]).  % i.e., at least one of

 owns(bob, not, 2).

 owns(bob, not, 3).

 hand(bob, Hand) :-
   % bob has two distinct cards:
   card(X), card(Y), X < Y, Hand = [X, Y],
   % if there is a "oneof" constraint, check it:
   (owns(bob, oneof, S) -> (member(A,S), member(A, Hand)) ; true),
   % check all the "not" constraints:
   ((owns(bob, not, Card), member(Card,Hand)) -> false; true).

使用以上内容的成绩单:

$ swipl
['disjunctions.pl'].
% disjunctions.pl compiled 0.00 sec, 9 clauses
true.

?- hand(bob,Hand).
Hand = [1, 4] ;
;
false.

请注意,Prolog是Turing完整的,所以一般来说,当有人说“它不能在Prolog中完成”时,它们通常意味着“它涉及一些额外的工作”。

答案 2 :(得分:2)

只是为了它,这是一个小程序:

card(1). card(2). card(3). card(4). % and so on

holds_some_of([1,2]). % and so on

holds_none_of([2,3]). % and so on

holds_card(C) :-
    card(C),
    holds_none_of(Ns),
    \+ member(C, Ns).

我省略了谁拥有什么等等。我没有故意将holds_some_of/1holds_none_of/1标准化。

这实际上足以用于以下查询:

?- holds_card(X).
X = 1 ;
X = 4.

?- holds_card(1).
true.

?- holds_card(2).
false.

?- holds_card(3).
false.

?- holds_card(4).
true.

这表明你甚至不需要Bob持有1或2的知识。顺便说一句,在尝试编写代码时,我注意到以下含糊不清的内容,来自原始问题陈述:

  

爱丽丝问鲍勃“你有1还是2?”,鲍勃向艾丽斯展示了一张卡片。查理现在得知鲍勃拥有1或2。

这是否意味着Bob确实一个为1和2,或者他可能持有一个或两个卡?

PS

上面的小程序实际上可以简化为以下查询:

?- member(C, [1,2,3,4]), \+ member(C, [2,3]).
C = 1 ;
C = 4.