2013-02-02 147 views
1

学习一些基本的序言,并且难以将我的头围绕逻辑​​。Prolog逻辑技巧

情景:个体感染病毒性脑膜炎并碰巧与其他人发生相互作用。这是我迄今为止的序言逻辑。

%-- 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(谁是最初患病)与斯文(假想的最后一个个体要生病)结束。我只是希望打印出两条可能的路径,而不包含无用的交互。例如。 tyrone会和别人说话,也不会说话。我也想删除重复(见下文)。

当我执行

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

首先,有一个在你提供的代码一个错字: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的“模型”中深处调用此“查看”代码。

你在括号中提到Ella是爆发的起源,但在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 

之所以詹姆斯没有达成解决办法,是因为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包裹谓词,然后你就会想通过结果来走,并打印出你需要的路径的部分。

编辑:在回答您的意见,让我们来看看你的新断言:

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). 

这并不十分有效,但它说的是,找到我一个来自发起者的路径,这样就没有其他长度更长的路径。这发现我们三个解决方案:

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

而这是尽可能接近我想我可以让你找到你想要的解决方案。我希望它有帮助!

+0

谢谢@丹尼尔里昂。实际上,我正在对不同的人工智能应用程序进行独立研究,而序言碰巧是有人建议的。 – user2035757

+0

现在关于路径,现在我有'路径(_,X,P): - findall(X,interact(_,X),P).'这给了我一个接近我预期的路径。这就是我现在得到的结果'[2] 23? - 路径(ella,X,Path)。 Path = [james,tyrone,ben,frank,carl,james,evan,mike,kelly | ...]。 – user2035757

+0

我已经添加了一些评论到最后讨论为什么这不是一个解决方案并告诉你什么是实际的解决方案看起来像。 –