我有一个我想解决的逻辑问题,所以我想,"我知道,我会尝试Prolog!"
不幸的是,我几乎立即遇到了一堵砖墙.其中一个假设是一个分离的事实; A,B或C是真的(或多于一个),但我不知道哪个.我已经知道这是Prolog不支持的.
那里有很多文档可以解决这个问题,但大多数文档似乎立即涉及更复杂的概念并解决了更高级的问题.我正在寻找的是一种模拟定义上述事实的孤立方式(由于Prolog的限制,不可能直接定义它).
我怎么能解决这个问题?我能以某种方式将它包装在规则中吗?
编辑:我意识到我还不是很清楚.鉴于我对Prolog缺乏熟悉,我不想在尝试传达问题时陷入语法错误,而是采用自然语言.我想这没有用,所以无论如何我都会在伪Prolog中试一试.
直观地说,我想要做的就是这样,宣布要么foo(a)
,foo(b)
或者说foo(c)
,但是我不知道哪个:
foo(a); foo(b); foo(c).
Run Code Online (Sandbox Code Playgroud)
然后我会期望以下结果:
?- foo(a); foo(b); foo(c).
true
Run Code Online (Sandbox Code Playgroud)
不幸的是,我试图宣布的事实(即foo(x)
至少保留一个x \in {a, b, c}
)不能这样定义.具体来说,它导致No permission to modify static procedure '(;)/2'
.
旁注:在宣布?- foo(a).
析取事实之后,从逻辑角度来看,我的结果有点不清楚; 它显然不是true
,但false
也没有涵盖它 - 在这种情况下,Prolog根本没有足够的信息来回答该查询.
编辑2:这里有更多的背景,使其更像一个真实的场景,因为我可能过度简化和丢失了翻译细节.
假设涉及三个人.爱丽丝,鲍勃和查理.鲍勃持有两张牌{1, 2, 3, 4}
.爱丽丝问他问题,回答这个问题,他向她展示了查理没看到的一张卡片,或者没有显示卡片.如果有更多卡适用,Bob只会显示其中一张卡.查理的任务是了解鲍勃持有的牌.正如人们所预料的那样,查理是一个自动化系统.
爱丽丝问Bob"你有1还是2?",鲍勃向Alice展示了一张卡片.查理现在得知鲍勃拥有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
; 查理可以得出这个.
小智 8
您问题的直接答案:
如果您可以使用有限域上的约束逻辑编程来建模您的问题,那么,可以使用#\
以下方式实现"异或" :
在三个变量X,Y,Z中,恰好一个可以在域1..3中.
D = 1..3, X in D #\ Y in D #\ Z in D
Run Code Online (Sandbox Code Playgroud)
为了概括这一点,你可以写:
disj(D, V, V in D #\ Rest, Rest).
vars_domain_disj([V|Vs], D, Disj) :-
foldl(disj(D), Vs, Disj, V in D #\ Disj).
Run Code Online (Sandbox Code Playgroud)
并将其用作:
?- 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))).
Run Code Online (Sandbox Code Playgroud)
如果您不使用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.
Run Code Online (Sandbox Code Playgroud)
查询读取:在列表中[A,B,C]
,其中一个变量可以是foo
,其余的必须与之不同foo
.您可以看到该查询的三种可能的解决方案.
遗憾的是,经常声称Prolog不支持这样或那样的事情; 通常,这不是真的.
你的问题目前还不是很清楚,但你说这意味着,通过这个程序:
foo(a).
foo(b).
foo(c).
Run Code Online (Sandbox Code Playgroud)
您将获得以下对查询的回答:
?- foo(X).
X = a ;
X = b ;
X = c.
Run Code Online (Sandbox Code Playgroud)
你可能解释为:
foo(a)
是真实的,并且foo(b)
是真实的,而且foo(c)
是真实的.
但是,如果我理解你的问题,你需要一个规则,例如:
正是其中之一
foo(a)
,foo(b)
而且foo(c)
可能是真的.
但是,根据上下文,它,您的程序的其余部分和您的查询,原始解决方案可能意味着!
但是你真的需要在你的问题中更具体,因为解决方案将取决于它.
以下是使用有限域上的约束编程解决该特定问题的解决方案,其中包括可在SWI-Prolog中获得library(clpfd)
的Markus Triska.
这是完整的代码:
:- 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).
Run Code Online (Sandbox Code Playgroud)
还有两个示例查询:
?- cards(1..4, [X,Y], [1 \/ 2 - yes, 2 \/ 3 - no]), X #= 1.
X = 1,
Y = 4 ;
false.
Run Code Online (Sandbox Code Playgroud)
如果这套牌是{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.
Run Code Online (Sandbox Code Playgroud)
如上所述,Charlie可以知道Bob是否持有3?
查理肯定知道鲍勃没有拿三个!
这是什么意思呢?
:- use_module(library(clpfd)).
Run Code Online (Sandbox Code Playgroud)
使库可用.
cards(Domain, Holds, QAs) :-
all_distinct(Holds),
Holds ins Domain,
maplist(qa_constraint(Holds), QAs).
Run Code Online (Sandbox Code Playgroud)
这定义了我们可以从顶层查询的规则.第一个参数必须是一个有效的域:在您的情况下,将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).
Run Code Online (Sandbox Code Playgroud)
"不"答案很简单:只要说对于Bob持有的每张卡片,它都不在提供的域中:#\ V in D
.
not_in(D, V) :- #\ V in D.
Run Code Online (Sandbox Code Playgroud)
"是"的回答意味着我们需要Bob持有的独家或全部卡片; 2\/3-yes
应该导致"第一张牌是2或3,或者第二张牌是2或3,但不是两张!"
disj(D, V, V in D #\ Rest, Rest).
Run Code Online (Sandbox Code Playgroud)
要了解最后一个,请尝试:
?- 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))).
Run Code Online (Sandbox Code Playgroud)