Prolog问题:计算时长的规则执行失败且不回溯重试
我正在学习Sterling和Shapiro所著的《The Art of Prolog》,遇到练习2.3.i:要求添加定义location(Course,Building)、busy(Lecturer,Time)和cannot_meet(Lecturer1,Lecturer2)关系的规则,并自行测试课程事实。我编写了相关规则如下:
duration(Course,Length) :- course(Course,time(Day,Start,Finish),Lecturer,Location),Start + Length = Finish. % Course 2 concludes before or right when Course 2 concludes cannot_meet(Lecturer1,Lecturer2) :- course(Course1,time(Day1,Start1,Finish1),Lecturer1,Location1),course(Course2,time(Day2,Start2,Finish2),Lecturer2,Location2),Finish1 >= Finish2, duration(Course2,C2Length),!, Start2 + C2Length >= Start1, Start2 + C2Length =< Finish1. %Course 1 ends first cannot_meet(Lecturer1,Lecturer2) :- course(Course1,time(Day1,Start1,Finish1),Lecturer1,Location1),course(Course2,time(Day2,Start2,Finish2),Lecturer2,Location2),Finish2 >= Finish1, duration(Course1,C1Length),!, Start1 + C1Length >= Start2, Start1 + C1Length =< Finish.
但执行时,Prolog在cannot_meet规则中调用duration时失败,且不会回溯重试。我是否误解了Prolog的回溯机制?它难道不应重试直到找到合适的Length满足条件吗?
问题根源与修复方案
1. 算术运算的错误用法
你在duration规则中用了Start + Length = Finish,这是Prolog的统一匹配操作,不是算术计算——它只会在Length已有绑定值时,检查Start+Length是否等于Finish,不会主动计算Length = Finish - Start。正确的算术计算要用is谓词:
duration(Course, Length) :- course(Course, time(Day, Start, Finish), Lecturer, Location), Length is Finish - Start.
2. 截断符!破坏回溯
你在duration调用后立刻加了!(截断符),这会直接清除所有之前的选择点(包括course查询的备选课程、duration的可能解)。一旦duration调用失败,Prolog没有回溯点可以返回重试,直接判定规则失败。必须去掉这个!,让Prolog能在当前组合不满足条件时,回溯尝试其他课程对。
3. 语法错误与逻辑冗余
第二个cannot_meet规则的最后一个条件里,Finish是未绑定的变量,应该写成Finish2;同时两个分情况的规则可以合并,直接用更简洁的时间段重叠判断逻辑:两个时间段重叠的核心条件是A课程开始于B课程结束前,且B课程开始于A课程结束前,不需要分谁结束得晚。
修复后的cannot_meet规则
cannot_meet(Lecturer1, Lecturer2) :- % 两个讲师有同一天的课程 course(Course1, time(Day, Start1, Finish1), Lecturer1, _), course(Course2, time(Day, Start2, Finish2), Lecturer2, _), % 时间段存在重叠 Start1 < Finish2, Start2 < Finish1.
关于回溯的理解
Prolog的回溯只会在未被截断的选择点上进行。你之前的!直接截断了所有回溯分支,导致失败后无法重试;去掉!后,当某个课程组合不满足重叠条件时,Prolog会自动回溯到course查询,尝试其他课程组合,直到找到符合条件的解或遍历完所有可能。
内容的提问来源于stack exchange,提问作者Jeremiah John

