Prolog - 计算旅行的总时间

时间:2015-05-20 15:27:42

标签: prolog

我正在尝试计算出所有地方出发和到达时的旅行总时间。

我正在尝试使用此功能来计算时间我将等待下一次传输:

time_out([(_,Hf1),(Hi2,Hf2)|R],To):-
   diff_t(Hi2,Hf1,To_I),
   time_out([(Hi2,Hf2)|R],To_R),
   To is To_R + To_I.

diff_t(Hi,Hf,D):-Hf>Hi, D is Hf - Hi.
diff_t(Hi,Hf,D):-Hf=<Hi,HCA is Hf + 2400, D is HCA - Hi.

当我测试它时:

?- time_out([(_,1300),(1400,1600)],T).
false.

为什么不给我我想要的总时间?

2 个答案:

答案 0 :(得分:1)

time_out/2谓词只有一个递归的子句。基本条款在哪里?这个递归子句遍历列表。假设使用闭合列表调用谓词,最终将使用空列表作为第一个参数调用谓词。由于没有任何条款与头部统一该目标,呼叫失败。

另请注意,您的子句不是尾递归的。即在这种情况下,在递归调用之后有一个目标。这意味着必须将此目标保存在堆栈中(对于每个递归调用),直到找到基本案例。这个浪费空间(线性un递归调用的数量)。在这种情况下(通常),将谓词转换为尾递归谓词的解决方案是使用累加器来计算您正在计算的距离。到达(缺少基本情况)时,累加器的值将是您正在计算的距离:

time_out(Stops, Distance) :-
    % the initial value of the accumulator is zero
    % as we're computing the sum of distances
    time_out(Stops, 0, Distance).

time_out([], Distance, Distance).
time_out([(_, End1), (Start2, End2)| Stops], Distance0, Distance) :- 
    diff_t(Start2, End1, Leg),
    Distance1 is Leg + Distance0,
    time_out([(Start2, End2)| Stops], Distance1, Distance).

但这个定义不正确。你能发现问题吗?当列表包含单个起始端对时会发生什么?你能纠正这个定义吗?也许我们有错误的基本情况?

大多数Prolog系统提供跟踪功能,这通常有助于理解Prolog计算:

?- trace.
true.

[trace]  ?- time_out([(_,1300),(1400,1600)],T).
   Call: (7) time_out([ (_G2156, 1300), (1400, 1600)], _G2169) ? creep
   Call: (8) time_out([ (_G2156, 1300), (1400, 1600)], 0, _G2169) ? creep
   Call: (9) diff_t(1400, 1300, _G2261) ? creep
   Call: (10) 1300>1400 ? creep
   Fail: (10) 1300>1400 ? creep
   Redo: (9) diff_t(1400, 1300, _G2261) ? creep
   Call: (10) 1300=<1400 ? creep
   Exit: (10) 1300=<1400 ? creep
   Call: (10) _G2262 is 1300+2400 ? creep
   Exit: (10) 3700 is 1300+2400 ? creep
   Call: (10) _G2265 is 3700-1400 ? creep
   Exit: (10) 2300 is 3700-1400 ? creep
   Exit: (9) diff_t(1400, 1300, 2300) ? creep
   Call: (9) _G2268 is 2300+0 ? creep
   Exit: (9) 2300 is 2300+0 ? creep
   Call: (9) time_out([ (1400, 1600)], 2300, _G2169) ? creep
   Fail: (9) time_out([ (1400, 1600)], 2300, _G2169) ? creep
   Fail: (8) time_out([ (_G2156, 1300), (1400, 1600)], 0, _G2169) ? creep
   Fail: (7) time_out([ (_G2156, 1300), (1400, 1600)], _G2169) ? creep
false.

仔细查看电话(9)。我会告诉你,直到明天你找到这个bug的解决方案。快乐的黑客!

<强>更新

现在OP找到了一个有效的解决方案是时候解决这个问题。例如,通过写:

time_out([(_, End1)| Stops], Distance) :-
    % the initial value of the accumulator is zero
    % as we're computing the sum of distances
    time_out(Stops, End1, 0, Distance).

time_out([], _, Distance, Distance).
time_out([(Start2, End2)| Stops], End1, Distance0, Distance) :- 
    diff_t(Start2, End1, Leg),
    Distance1 is Leg + Distance0,
    time_out(Stops, End2, Distance1, Distance).

请注意,大多数Prolog系统都实现了一个名为 first-argument indexing 的优化,它允许尝试可以解析当前目标的子句。这种优化通常通过考虑类型来实现,对于原子术语,在某些情况下,通过考虑第一个参数的值(如果绑定)来实现。 time_out/4谓词的两个子句中的第一个参数,对于第一个,原子 [](空列表 a列表),第二个是带有一个或多个元素的列表。因此,假设这种优化,每次调用这个谓词时都会选择正确的子句(当然,它的第一个参数被实例化),从而避免虚假的选择点。但是,对于您的解决方案,不能应用相同的优化,因为在time_out/2谓词的两个子句中,第一个参数是列表。

答案 1 :(得分:1)

正如Paulo所说,你需要一个递归的基本子句:

time_out([_], 0).
time_out([(_, Hf1), (Hi2, Hf2)|R], To) :- 
              diff_t(Hi2, Hf1, To_I),
              time_out([(Hi2, Hf2)|R], To_R),
              To is To_R + To_I.