SWI-Prolog中shipmates谓词变量查询无结果问题求助
问题分析与修正方案
咱们先拆解一下你遇到的两个问题:
1. shipmates 谓词的问题
你的 shipmates 定义是:
shipmates(A, B) :- A \= B, aboard(A, X), aboard(B, X).
问题出在目标执行顺序上。Prolog是按顺序执行目标的,A \= B 被放在了最前面——当你查询 shipmates(donna, X) 时,B 还是一个未绑定的变量,这时候 donna \= X 会直接失败。
为什么?因为Prolog的 \= 是当前实例化状态下的不等测试:如果两个项还能被统一(比如一个是原子,一个是未绑定变量),它就会返回false。这里X可以被绑定为donna,所以donna \= X不成立,整个目标直接失败,后面的aboard查询根本没机会执行。
2. shipmates1 谓词的问题
你的 shipmates1 定义:
shipmates1(A, A) :- !, fail. shipmates1(A, B) :- aboard(A, X), aboard(B, X).
这里的坑是切断符!。当你查询 shipmates1(donna, X) 时,Prolog首先尝试第一个子句 shipmates1(A,A):它会把X绑定为donna(让两个参数相等),然后执行!——这个切断符会直接终止所有回溯,接着fail让整个查询直接失败,完全不会去尝试第二个子句。这就是为什么它和shipmates一样返回false。
修正后的代码
两种方案都可以,最简洁的是调整shipmates的目标顺序,先绑定同船关系,再过滤自身:
aboard(jack, blackbird). aboard(jim, blackbird). aboard(donna, blackbird). aboard(david, north_star). aboard(sandy, north_star). shipmates(A, B) :- aboard(A, Ship), % 先找到A所在的船 aboard(B, Ship), % 再找同船的B A \= B. % 最后过滤掉和A相同的情况
测试一下:
- 查询
shipmates(donna, X)会返回X = jack ; X = jim,符合预期。 - 查询
shipmates(jack, jack)会返回false,也符合要求。
如果你想用子句拆分的方式实现类似shipmates1的逻辑,记得去掉切断符或者调整逻辑,比如:
shipmates1(A, B) :- aboard(A, Ship), aboard(B, Ship), A == B, % 先判断是否是同一个人 fail. % 如果是,直接失败 shipmates1(A, B) :- aboard(A, Ship), aboard(B, Ship).
不过这种方式不如第一种简洁,推荐用第一种方案。
关键知识点总结
- Prolog目标顺序很重要:一定要把需要绑定变量的目标放在前面,再做条件过滤。
\=vs==:\=检查的是“当前能否统一”,==检查的是“是否完全相同”。在判断两个项是否不等时,一定要等变量绑定后再用\=。
内容的提问来源于stack exchange,提问作者Zach Blocker
相关产品推荐
相关产品推荐

