桌子上有六个座位

时间:2019-07-04 10:39:53

标签: prolog

我想限制坐在一张桌子上的人数(准确地说是6人),但是通过序言,我不知道该怎么做

我已经尝试使用between (0, 5, W), 但是我不明白为什么每次使用活动轨迹时都会看到它随着W的增加而前进,但是却无法退出下一个函数

(W =: = 0 -> nb_setval (counter, 0), W is 5,
person(antonella).
person(domenico).
person(raffaella).
person(tommaso).
person(vincenzo).
person(azzurra).
person(cristiano).
person(francesca).
person(luigi).
person(giovanni).
person(marcella).
person(daniela).
person(nunzio).
person(leonardo).
person(silvia).
notinfamily(giovanni).
notinfamily(marcella).
notinfamily(daniela).
notinfamily(nunzio).
notinfamily(leonardo).
%declare fight
fight(giovanni, marcella).
fight(marcella, daniela).
fight(luigi, leonardo).
%declare the familys
family(antonella).
family(domenico).
family(raffaella).
family(tommaso).
family(vincenzo).

family1(azzurra).
family1(cristiano).
family1(francesca).
family1(luigi).

sitdown :-
    (   person(X);family(X)),
    \+ fight(X,_),
    \+ fight(_,X),
    between(0, 5, W),
    (   W =:= 0 -> nb_setval(counter,0), W is 5,
    writeln(W),
    nb_getval(counter,Countval),
    writeln(Countval)).

我希望该功能在lf之后继续(在序言中应为“->”),但始终保持在该位置,而无需进行任何操作。我可以插入Trace返回给我的内容。

1 个答案:

答案 0 :(得分:1)

我的解决方案不完整。但我希望这足以给您一些希望弄清其余的内容。首先,我稍微回顾一下您的数据模型:

family(antonella,1).
family(domenico,1).
family(raffaella,1).
family(tommaso,1).
family(vincenzo,1).

family(azzurra,2).
family(cristiano,2).
family(francesca,2).
family(luigi,2).

我还删除了notinfamily/1,因为您有一个不在家庭或非家庭中的人,这花费了我一些调试时间。 :)更好地进行推断。

接下来,我们需要几个帮手。首先,为您的数据模型提供一些帮助程序,以使其更容易确定家庭是否匹配或人们是否在战斗。这些应该很容易理解,我只是确保这些家庭确实匹配,或者其中一个不在一个家庭中(我们通过否定而不是notinfamily/1进行检查):

family_matches(P1, P2) :-
    family(P1, Family),
    family(P2, Family).
family_matches(P1, P2) :-
    \+ family(P1, _) ; \+ family(P2, _).

战斗目前不具有关联性,所以我提出了一个独立的关联性谓词:

fighting(X, Y) :- fight(X, Y) ; fight(Y, X).

最初,我使用select/3确实是从列表中删除各种项目的一种好方法,但是我意识到做不同的排列会杀死我,所以我想出了这个版本,我只能使用生成组合:

select_inorder(X, [X|Xs], Xs).
select_inorder(X, [_|Rem], Xs) :-
    select_inorder(X, Rem, Xs).

此谓词的使用将在一秒钟内变得清楚。

如果可以避免,我宁愿不要在Prolog中涉及算术。乍一看,这就像是一种情况,您可以假设将有3张桌子,每张桌子5张,一切都会很棒。事实并非如此,但我想无论如何我都会分享我编写的代码,以防它帮助您解决问题。我的计划是这样的:我将拥有一个谓词,知道它正在组装五个人的名单。首先,我将把您的数据库变成一个人员列表。我将从列表中选择人员。当我有五个时,我将验证该表是否有效。我将有一个主要谓词来进行记账并运行此表生成器三次。

seating([T1,T2,T3]) :-
    findall(X, person(X), People),
    make_table(People, T1, R1),
    make_table(R1, T2, R2),
    make_table(R2, T3, []).

可以用DCG语法更清楚地重申这一点。但这对您来说应该是明智的。制作桌子有类似的味道:

make_table(People, [A,B,C,D,E], Unseated) :-
    select_inorder(A, People, P1),
    select_inorder(B, P1, P2),
    select_inorder(C, P2, P3),
    select_inorder(D, P3, P4),
    select_inorder(E, P4, Unseated),
    safe_table([A,B,C,D,E]).

select_inorder/3用于确保我们按顺序选择人员,否则会浪费时间。一旦我们有五个人,我们将对其进行验证。这是一个经典的“生成测试”循环。

safe_table(Table) :-
    forall((member(L, Table), member(R, Table)),
           (family_matches(L, R), \+ fighting(L, R))).

我有点喜欢这里的逻辑阅读。这不是超级有效,它将对列表中的所有项目产生叉积。但是,我认为这样的事情可能是有必要的,因为family_matches不是可传递的(family_matches(antonella, giovanni), family_matches(giovanni, azzurra)是正确的,而family_matches(antonella, azzurra)是不正确的。)

当然,现在令人讨厌的是,这行不通。但是它不起作用的原因很简单:我们将所有家庭1放在一张桌子上,并将家庭2全部放在另一张桌子上之后,我们有一个人搬到他们的桌子上,然后我们必须找到五个人的组合其余的人坐在一起。但是不幸的是,不在一个家庭中的三个人正在互相争斗。如果您可以使marcella和daniela相处融洽,那么您将得到一种解决方案:

?- seating([T1,T2,T3]).
T1 = [antonella, domenico, raffaella, tommaso, vincenzo],
T2 = [azzurra, cristiano, francesca, luigi, giovanni],
T3 = [marcella, daniela, nunzio, leonardo, silvia] ;
false.

对不完整的表格进行这项工作对我来说有点繁琐,所以我将其保留在原处,希望它仍然对您有所帮助。