Prolog逻辑提示

时间:2013-02-02 18:41:02

标签: prolog logic

学习一些基本的序言,我很难绕过逻辑。

情景:一个人感染病毒性脑膜炎,并恰好与其他各种人进行互动。到目前为止,这是我的序言。

%-- Set a sickness condition.
%-- -------------------------------------------------------- --%
setILL(X) :- write(X), write(' is ill'), nl.

%-- Link some interactions between individuals.
%-- -------------------------------------------------------- --%
interact(ella, james).
interact(ella, tyrone).
interact(james, ben).
interact(james, frank).
interact(james, carl).
interact(carl, james).
interact(carl, evan).
interact(evan, mike).
interact(evan, kelly).
interact(mike, frank).
interact(kelly, carl).
interact(kelly, frank).
interact(kelly, ben).
interact(sven, mike).

%-- Create an interaction condition.
%-- -------------------------------------------------------- --%
came_in_contact(X, Y) :- setILL(X), write(X), write(' has had contact with '), write(Y), X\=Y, !, nl.  

%-- Create a rule for sickness
%-- -------------------------------------------------------- --%
sick(X) :- interact(X, Y), contact(X, Y), Y\=X.  
whosick(R) :- findall([X], sick(X), R).

现在互动是他们应该做的,并且应该有两条路径,每条路径都以ella(最初生病)开始,最后是sven(假设最后一个人生病)。我只想打印出两条可能的路径而不包括无用的交互。例如。泰隆将与任何其他人交谈,而且也不会。我也希望删除重复(见下文)。

执行时

whosick(X).

我得到了

ella is ill
ella has had contact with james
ella is ill
ella has had contact with tyrone
james is ill
james has had contact with ben
james is ill
james has had contact with frank
james is ill
james has had contact with carl
carl is ill
carl has had contact with james
carl is ill
carl has had contact with evan
evan is ill
evan has had contact with mike
evan is ill
evan has had contact with kelly
mike is ill
mike has had contact with frank
kelly is ill
kelly has had contact with carl
kelly is ill
kelly has had contact with frank
kelly is ill
kelly has had contact with ben
sven is ill
sven has had contact with mike
X = [[ella], [ella], [james], [james], [james], [carl], [carl], [evan], [...]|...].

1 个答案:

答案 0 :(得分:1)

首先,您提供的代码中存在拼写错误:came_in_contact应该是contact或者它不会运行。小问题。

第二个问题:我不确定您的意思:findall([X], sick(X), R).此处没有使用[X]而非X的特殊理由,以及结果看起来好一点:

X = [ella, ella, james, james, james, carl, carl|...].

更重要的一个问题是,在风格上,setill是100%的副作用,尽管名声有状态。 setill没有"设置"任何人"生病,"它只是打印到标准输出,有人生病了。你可能会说,这是"视图"的一部分。如果这是MVC。因此,您遇到的部分问题是,您正在调用此问题"查看"来自"模型内的代码,"在sick/2

你提到称,艾拉是爆发的起源,但你的Prolog数据库中没有任何事实,所以Prolog肯定不知道它。此外,您似乎对"路径"感兴趣。感染了,但你的Prolog对路径一无所知 - 事实上,它只是倾销你的事实数据库。为了证明这一点,让我们在顶部添加一个新事实:

interact(gail, hank).

果然,它现在是第一个"解决方案"即使Gail和Hank与图表的其余部分隔离开来:

gail is ill
gail has had contact with hank
… (old output repeated)

...

所以,你在这里的杂草中解脱了。你有一个不完整的事实数据库,你的规则并没有真正捕捉到问题的逻辑,而且它们在逻辑与印刷之间散布着。当我们在这里完成代码时,代码会变得非常不同。我不确定这是否是家庭作业,听起来像是你自学,但它有一种家庭作业的氛围,所以我要试着勾勒出我将如何继续没有把它们放在一起。


首先,您需要让Prolog知道所有计算解决方案所需的事实。也就是说,你必须添加关于发起人的事实:

infected(ella) :- !.

这将成为基本案例。现在我们需要采用归纳推理,并说如果一个人与受感染者有过接触,就会被感染:

infected(X) :- interact(X, Y), X \= Y, infected(Y), !.

注意:这些削减相当重要。没有必要计算另一个解决方案,因为一个人是否被感染。如果我们在任何一个分支上取得成功,证明他们已经被感染,那就没什么好说的了。

现在我们可以为某些人找到合理的解决方案:

?- infected(ella).
true.

?- infected(gail).
false.

其他人似乎没有解决方案:

?- infected(james).
(I typed Ctrl+C)
^CAction (h for help) ? abort
% Execution Aborted

詹姆斯没有找到解决方案的原因是因为Prolog正在使用深度优先搜索。简而言之,您要做的下一件事就是发现感染路径,因此如果您可以阻止Prolog尝试已经在路径中的人,您可以通过获取您还需要的路径来解决问题。您将不得不采用类似的基础案例/归纳案例结构,但为感染途径传递额外的论据。你可以找到这种事情的例子,所以我不会在这里为你详细介绍。

请注意这一点:我们要将问题的逻辑与显示结果混合在一起。由于回溯,这对Prolog来说是一个很好的政策。如果你打印出一些东西,因为这里的绑定成功,并且在下一个术语中它失败了,整个失败可能会回到打印输出,留下一个困惑的用户。我们可以轻易地欺骗Prolog打印出以后失败的解决方案的谎言。因此,总是想要编写Prolog,以便找到解决方案然后单独显示它们。想想模型 - 视图 - 控制器。

因此,我们假设您找到了谓词path/3(大概是path(Source, Last, Path))。当你运行它时,你会得到这样的解决方案:

?- path(ella, X, Path).
X = sven
Path = [ella, james, ...] ;

X = sven
Path = [ella, tyrone, ...] ;
false.

这是您希望用findall/3打包的谓词,然后您想要遍历结果并打印出您需要的路径。< / p>

修改:在回复您的评论时,让我们看看您的新谓词:

path(_, X, P) :- findall(X, interact(_, X), P).

我担心这不比以前更接近。让我们看看当我向自己询问道路时会发生什么:

?- path('Daniel Lyons', X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].

事实上,你可以把任何东西放在那里,你会得到完全相同的结果:

?- path('Jack Donaghy', X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
?- path(3.1415926, X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].
?- path([a,b,c,d,e], X, Path).
Path = [james, tyrone, ben, frank, carl, james, evan, mike|...].

这是因为您的规则适用于第一个位置的任何。如果你有更多的条款,这可能是有意义的,因为其中一个条款可以说出这个论点,但缺乏它确实意味着什么。所以你的谓词也可以写成:

path(X, P) :- findall(X, interact(_, X), P).

每个_都是一个完全独特的绑定;他们根本不会相互影响,所以如果你希望在那里产生影响,你就会想要更像这样的东西:

path(F, X, P) :- findall(X, interact(F, X), P).

你马上就知道这对你没有多大帮助:

?- path(ella, X, P).
P = [james, tyrone].

所以我们已经解决了这个问题。

person(X) :- interact(X, _) ; interact(_, X).

这只是一个帮助所有人的帮助者,无论他们是在交互的左侧还是右侧。

path(Originator, Path) :- 
  setof(X, person(X), People), 
  path(Originator, Path, People).

这个助手会让你从特定的人那里获得一条路径。我们依赖于我将在一秒钟内展示的辅助功能。我们从所有人的名单开始,修剪一些合理的可能性。这样我们就可以从我们尚未检查过的人员列表中选择下一个人,而且我们不必担心周期或过多的递归。

path(Originator, [], _).
path(Originator, [NextPerson|Rest], Considering) :-
  select(NextPerson, Considering, RemainingToConsider),
  interact(Originator, NextPerson), 
  path(NextPerson, Rest, RemainingToConsider).

第一个条款说,我们总能完成。从始发者到无人的路径是空路径。这是我们归纳的基础案例。

第二个条款说,从我们留下的人员名单中选择一个人来考虑。有人与创始人互动。现在找到一个从那个人到剩下的人要考虑的路径。 (select/3将第三个参数与没有第一个参数的第二个参数统一起来。)

让我们看一下跑步:

?- path(ella, X).
X = [] ;
X = [james] ;
X = [james, ben] ;
X = [james, carl] ;
X = [james, carl, evan] ;
X = [james, carl, evan, kelly] ;
X = [james, carl, evan, kelly, ben] ;
X = [james, carl, evan, kelly, frank] ;
X = [james, carl, evan, mike] ;
X = [james, carl, evan, mike, frank] ;
X = [james, frank] ;
X = [tyrone] ;
false.

现在,在你原来的问题中,你说了一些关于本和弗兰克的事情,而不是对其他路径感兴趣。我仍然没有看到能够区分这些案例的逻辑阅读,但你至少可以找到所有最长的路径,如下所示:

longest_paths(Originator, Path) :-
  path(Originator, Path),
  \+ (path(Originator, Path2), 
      length(Path, MaxLen), 
      length(Path2, NextLen), 
      NextLen > MaxLen).

这并不是非常有效,但它说的是,找到一条来自Originator的路径Path,这样就没有其他路径有更长的长度。这找到了三个解决方案:

?- longest_paths(ella, X).
X = [james, carl, evan, kelly, ben] ;
X = [james, carl, evan, kelly, frank] ;
X = [james, carl, evan, mike, frank] ;
false.

这就像我认为可以让你找到你想要的解决方案一样接近。我希望它有所帮助!