Prolog逻辑提示

Prolog逻辑提示,prolog,logic,Prolog,Logic,学习一些基本的序言,我很难理解逻辑 情景:一个人感染病毒性脑膜炎,并碰巧与其他不同的人相互作用。到目前为止,这是我的序言逻辑 %-- Set a sickness condition. %-- -------------------------------------------------------- --% setILL(X) :- write(X), write(' is ill'), nl. %-- Link some interactions between individuals

学习一些基本的序言,我很难理解逻辑

情景:一个人感染病毒性脑膜炎,并碰巧与其他不同的人相互作用。到目前为止,这是我的序言逻辑

%-- 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(最后生病的人)结束。我只是希望打印出两条可能的路径,而不包括无用的交互。泰龙不会和别人说话,本也不会。我还希望删除REPEAST(见下文)

当我执行

whosick(X).
我明白了


首先,您提供的代码中有一个输入错误:
comein\u 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 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
James没有找到解决方案的原因是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.
path(F, X, P) :- findall(X, interact(F, X), P).
longest_paths(Originator, Path) :-
  path(Originator, Path),
  \+ (path(Originator, Path2), 
      length(Path, MaxLen), 
      length(Path2, NextLen), 
      NextLen > MaxLen).
这是您需要用
findall/3
包装的谓词,然后您需要遍历结果并逐路径打印出需要的部分

<> > >编辑< /强>:响应你的评论,让我们看看你的新谓词:

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(ella, X, Path).
X = sven
Path = [ella, james, ...] ;

X = sven
Path = [ella, tyrone, ...] ;
false.
path(F, X, P) :- findall(X, interact(F, X), P).
longest_paths(Originator, Path) :-
  path(Originator, Path),
  \+ (path(Originator, Path2), 
      length(Path, MaxLen), 
      length(Path2, NextLen), 
      NextLen > MaxLen).
你马上就会发现这对你没有多大帮助:

?- 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).
这个助手可以从特定的人那里获取路径。我们依赖于一个助手函数,我将在稍后展示。我们从所有人的名单开始,把可能性删减到合理的程度。这样我们就可以从我们还没有检查过的人列表中选择下一个人,而我们没有
?- longest_paths(ella, X).
X = [james, carl, evan, kelly, ben] ;
X = [james, carl, evan, kelly, frank] ;
X = [james, carl, evan, mike, frank] ;
false.