4

假设我有这个知识库:

free(ann,slot(time(8,0),time(9,0))).
free(ann,slot(time(10,0),time(11,0))).

free(bob,slot(time(7,0),time(8,30))).
free(bob,slot(time(10,0),time(11,0))).

free(carla,slot(time(8,0),time(9,0))).
free(carla,slot(time(10,0),time(10,15))).

因此,经过大量努力,我设法使用以下代码编写了一些内容,用于打印在特定时段内有空的第一个人:

meetone(Person, slot(time(BeginHour, BeginMinute), time(EndHour, EndMinute)))
:- free(Person, slot(time(BH, BM), time(EH, EM))),
   BH*60 + BM =< EndHour*60 + EndMinute,
   EH*60 + EM >= BeginHour*60 + BeginMinute.

main :- (meetone(Person,slot(time(7,15),time(7,20))); halt),
       write(Person),
       nl,
       halt.

:- initialization(main).

这将打印 bob,即预期的结果。

这就是它变得复杂的地方(至少对我来说)。假设我想找出知识库中每个人共有的所有时间段。以下代码演示了我最终要如何调用它:

people([ann,bob,carla]).

meet :- ???

main :- (setof(Slot,meet(Slot),Slots); halt),
        write(Slots),
        nl,
        halt.

:- initialization(main).

这是一些模糊的伪代码,我认为它们可能会完成我正在寻找的东西,但我没有足够的经验来让它工作。

  1. 从第一个人的空闲位置开始。
  2. 递归遍历人员列表的其余部分,找到重叠时间。
  3. 可以使用类似于 meetone 的东西来验证插槽是否重叠;在此验证之后,两个时隙的开始时间的最大值和两个结束时间的最小值可用于找到确切的重叠时段。
  4. 打印最终的插槽列表。

最终输出将显示 8:00 - 8:30 和 10:00 - 10:15 的时段。任何帮助实现这一点将不胜感激。

4

1 回答 1

1

Prolog 有一些语法特性,恕我直言,它们确实有助于编写可理解的代码。然后这是您的问题,利用运营商获得可读性:

free(ann, 08:00 > 09:00).
free(ann, 10:00 > 11:00).

free(bob, 07:00 > 08:30).
free(bob, 10:00 > 11:00).

free(carla, 08:00 > 09:00).
free(carla, 10:00 > 10:15).

meetone(Person, Slot) :- free(Person, SlotP), contains(SlotP, Slot).

contains(Slot1, Slot2) :-
   timepoints(Slot1, B1, E1),
   timepoints(Slot2, B2, E2),
   B1 =< E2, E1 >= B2.

timepoints(BH:BM > EH:EM, B, E) :-
   B is BH*60 + BM,
   E is EH*60 + EM.

main :- setof(Person, meetone(Person, 7:15 > 7:20), Available),
    maplist(writeln, Available).

我试图介绍两个实用程序,一种可重用的代码:contains/2 和 timepoints/3。在编写更复杂的逻辑时,拥有这样的片段可能会有所帮助......

运行这段代码,我得到

?- main.
bob
true.

现在来到你的主要问题:

假设我想找出知识库中每个人共有的所有时间段

我将开始编写 common_timeslot/3,解释(计算)预期内容:

common_timeslot(S1, S2, Common) :-
   timepoints(S1, B1, E1),
   timepoints(S2, B2, E2), 
   % do you mean intersection by common ?
   ...

否则,只考虑身份

common_timeslot(S, S, S).

定义了这个之后,所有的公共都可以找到

main :-
    setof(Sc/P/Q, Sp^Sq^(
        free(P, Sp), free(Q, Sq), Q \= P,
        common_timeslot(Sp, Sq, Sc)
    ), Commons),
    maplist(writeln, Commons).

产生

?- main.
(8:0>9:0)/ann/carla
(8:0>9:0)/carla/ann
(10:0>11:0)/ann/bob
(10:0>11:0)/bob/ann
true.

编辑mat 评论的会计,现在我发布整个程序

free(ann, 08:00 < 09:00).
free(ann, 10:00 < 11:00).

free(bob, 07:00 < 08:30).
free(bob, 10:00 < 11:00).

free(carla, 08:00 < 09:00).
free(carla, 10:00 < 10:15).

meetone(Person, Slot) :- free(Person, SlotP), contains(SlotP, Slot).

contains(Slot1, Slot2) :-
   timepoints(Slot1, B1, E1),
   timepoints(Slot2, B2, E2),
   B1 =< E2, E1 >= B2.

timepoints(BH:BM < EH:EM, B, E) :-
    (   ( var(B), var(E) )
    ->  B is BH * 60 + BM,
        E is EH * 60 + EM
    ;   BH is B // 60,
        BM is floor(B mod 60),
        EH is E // 60,
        EM is floor(E mod 60)
    ).

% common_timeslot(S, S, S).
common_timeslot(S1,S2,S) :-
    timepoints(S1,B1,E1),
    timepoints(S2,B2,E2),
    B is max(B1,B2),
    E is min(E1,E2),
    B < E,
    timepoints(S,B,E).

% base case: C passed all commonality test
check_common(C, [], C).

% S is the current common, to be checked for availability on person P
check_common(S, [P|Ps], C) :-
    free(P, Sp),
    common_timeslot(S, Sp, Ct),
    check_common(Ct, Ps, C).

main :- setof(P, S^free(P,S), [FirstP|Others]),
    forall(free(FirstP, S), (
        check_common(S, Others, C),
        writeln(FirstP:C)
    )).

产生

?- main.
ann: (8:0<8:30)
ann: (10:0<10:15)
true.

主要变化是 timepoints/3 现在是“双向”的。然后我介绍了 common_timeslot/3 正如您在评论中解释的那样。

我想你会明白那些小的句法抽象有助于拥有一个干净的“应用”逻辑。当然,forall /2 或setof /3 是您需要学习的内置函数,以便更熟练地使用 Prolog。

于 2013-10-20T10:18:53.467 回答