Prolog水排序解谜器异常:重复结果、状态残留与GoalCheck不一致
Prolog水排序解谜程序异常排查与修复
问题1:查询goal(S)首次返回S=s0,按分号得false,再次查询才输出目标状态
原因
- search谓词逻辑偏差:当前
search(S)的判定逻辑是「S是目标状态,或从S可抵达目标状态」,而非「S是从初始状态s0出发能到达的目标状态」。查询goal(S)时,Prolog会优先将S绑定为s0(因为s0可抵达目标),而非直接返回最终目标状态。 - 动态事实未自动清理:
pour操作通过assert永久向数据库添加中间状态的bottle事实,第一次查询后这些事实残留,第二次查询时直接匹配到目标状态的事实,才返回正确结果。
解决方案
- 重构
search为带状态传递的递归谓词,明确从初始状态s0出发,追踪并返回最终目标状态; - 添加状态清理逻辑,每次查询前清空所有动态
bottle事实(除初始状态),避免残留事实干扰结果。
问题2:执行goal(S)后查询goalCheck(S)返回状态值而非布尔值,初始加载时查询则返回false
原因
- 初始状态不满足目标:初始状态
s0下没有两个瓶子装满同色液体,goalCheck(s0)返回false符合预期; - Prolog变量绑定特性:执行
goal(S)后S已被绑定为某个状态,此时查询goalCheck(S)会尝试匹配目标条件,若匹配成功,Prolog会输出满足条件的变量绑定(如C1=b, C2=r),而非单纯的true——这是Prolog默认行为,当谓词成功且存在未绑定变量时,会输出变量绑定结果。
解决方案
- 若需强制获取布尔结果,可查询
(goalCheck(S) -> true; false),无论变量是否绑定,都会返回true或false; - 确保
goal(S)返回的S是目标状态而非初始状态,此时goalCheck(S)会直接返回true(或确定的变量绑定值)。
问题3:修改瓶子初始内容后重新consult,结果无变化,需重启后再consult才生效
原因
动态事实未被清理:初始的bottle(..., s0)事实是静态定义的,consult时不会覆盖已存在的动态bottle事实(包括之前pour操作添加的中间状态事实),新的初始状态被旧事实干扰,导致结果无变化。
解决方案
- 添加
clear_bottles/0谓词,用于清理所有bottle事实并重新加载初始状态,每次修改初始内容或查询前调用该谓词:clear_bottles :- retractall(bottle(_, _, _, _)), % 重新初始化初始状态 bottle(1, b, r, s0), bottle(2, b, r, s0), bottle(3, e, e, s0). - 或将初始状态的
bottle事实改为动态,确保consult时旧事实被完全替换。
修改后的完整代码
:- dynamic bottle/4. % 清理所有瓶子事实并重新初始化初始状态 clear_bottles :- retractall(bottle(_, _, _, _)), bottle(1, b, r, s0), bottle(2, b, r, s0), bottle(3, e, e, s0). % Valid pour action: The action is valid if: % 1. The destination bottle is not full (i.e., its top and bottom colors are not the same). % 2. The source bottle is not empty (i.e., it has a color in the top layer). % 3. The top color layers of both the source and destination bottles are the same. % 4. If the destination bottle is empty, pouring is always valid as long as the source bottle is not empty. validPour(Source, Destination, S) :- bottle(Source, TopColor, BotColorSrc, S), bottle(Destination, TopColorDest, BotColorDest, S), \+ isEmpty(Source, S), ( (TopColor \= e, (isEmpty(Destination, S) ; (TopColorDest == e, BotColorDest == TopColor))) ; (TopColor == e, BotColorSrc \= e, (isEmpty(Destination, S) ; (TopColorDest == e, BotColorDest == BotColorSrc))) ). isEmpty(Bottle, S) :- bottle(Bottle, e, e, S). % Pour action: Pour from bottle i to bottle j(使用assertz保证事实添加顺序) pour(Source, Destination, Third, S, NextState) :- (bottle(Source, Color1, BotColorSrc, S) ; bottle(Source, e, Color1, S)), bottle(Destination, Color2, BotColorDest, S), bottle(Third, Color3, BotColorThird, S), NextState = result(pour(Source, Destination), S), Color1 \= e, % 更新目标瓶子状态 (isEmpty(Destination, S) -> assertz(bottle(Destination, e, Color1, NextState)) ; (BotColorDest \= e, BotColorDest == Color1, Color2 == e -> assertz(bottle(Destination, Color1, BotColorDest, NextState)) ) ), % 更新源瓶子状态 (bottle(Source, Color1, BotColorSrc, S) -> assertz(bottle(Source, e, BotColorSrc, NextState)) ; bottle(Source, e, Color1, S) -> assertz(bottle(Source, e, e, NextState)) ), % 更新第三个瓶子状态 assertz(bottle(Third, Color3, BotColorThird, NextState)). goalCheck(S) :- % Case 1: Bottles 1 and 2 are uniform and full, Bottle 3 is empty (bottle(1, C1, C1, S), bottle(2, C2, C2, S), bottle(3, e, e, S), C1 \= e, C2 \= e, C1 \= C2) ; % Case 2: Bottles 2 and 3 are uniform and full, Bottle 1 is empty (bottle(2, C1, C1, S), bottle(3, C2, C2, S), bottle(1, e, e, S), C1 \= e, C2 \= e, C1 \= C2) ; % Case 3: Bottles 1 and 3 are uniform and full, Bottle 2 is empty (bottle(1, C1, C1, S), bottle(3, C2, C2, S), bottle(2, e, e, S), C1 \= e, C2 \= e, C1 \= C2). % 重构search:从指定状态出发,递归寻找目标状态 search(CurrentState, GoalState) :- goalCheck(CurrentState), GoalState = CurrentState. search(CurrentState, GoalState) :- \+ goalCheck(CurrentState), ( (validPour(1, 2, CurrentState), pour(1, 2, 3, CurrentState, NextState), search(NextState, GoalState)) ; (validPour(1, 3, CurrentState), pour(1, 3, 2, CurrentState, NextState), search(NextState, GoalState)) ; (validPour(3, 1, CurrentState), pour(3, 1, 2, CurrentState, NextState), search(NextState, GoalState)) ; (validPour(3, 2, CurrentState), pour(3, 2, 1, CurrentState, NextState), search(NextState, GoalState)) ; (validPour(2, 3, CurrentState), pour(2, 3, 1, CurrentState, NextState), search(NextState, GoalState)) ; (validPour(2, 1, CurrentState), pour(2, 1, 3, CurrentState, NextState), search(NextState, GoalState)) ). % goal/1:先清理状态,再从初始状态s0开始搜索目标状态 goal(GoalState) :- clear_bottles, search(s0, GoalState).
使用说明
- 查询目标状态:直接调用
goal(S),会返回从s0出发到达的目标状态; - 修改初始状态:修改
clear_bottles中的初始bottle断言,调用clear_bottles或重新consult文件即可生效; - 检查状态是否为目标:调用
(goalCheck(S) -> true; false)获取布尔结果,或直接调用goalCheck(S)查看变量绑定。
内容的提问来源于stack exchange,提问作者Ahmed Haitham
相关产品推荐
相关产品推荐

