Prolog中successor查询结果重复与缺失问题排查
问题场景
以下是编写的Prolog代码:
male(prince_charles). male(prince_andrew). male(prince_edward). female(princess_ann). female(queen_elizabeth). child_of(prince_charles, queen_elizabeth). child_of(princess_ann, queen_elizabeth). child_of(prince_andrew, queen_elizabeth). child_of(prince_edward, queen_elizabeth). throne(queen_elizabeth). older(prince_charles, princess_ann). older(prince_charles, prince_andrew). older(prince_charles, prince_edward). older(prince_andrew, prince_edward). older(princess_ann, prince_andrew). older(princess_ann, prince_edward). successor(X) :- male(X), older(X, Y), not(female(Y)), child_of(X, A), child_of(Y, A), throne(A). successor(X) :- female(X), child_of(X, Y), throne(Y).
执行findall(X,successor(X), List).得到结果:List = [prince_charles, prince_charles, prince_andrew, princess_ann].,但预期结果为List = [prince_charles, prince_andrew, prince_edward, princess_ann].
问题原因
1. prince_charles重复出现
第一个successor/1规则中,当X匹配prince_charles时,Y可以匹配两个符合条件的男性兄弟:prince_andrew和prince_edward。Prolog的回溯机制会为每个有效的Y生成一条独立的证明路径,因此prince_charles会被重复返回两次。
2. prince_edward未被匹配
第一个规则要求older(X, Y)且Y是男性(not(female(Y))),但prince_edward是女王最小的儿子,不存在比他年幼的男性兄弟,找不到满足条件的Y,无法触发第一个规则;第二个规则仅针对女性,prince_edward是男性,也无法触发,因此完全不被匹配。
修正方案
原规则错误地将“存在比自己年幼的男性兄弟”作为男性继承人的前提,实际应该以“没有更年长的男性兄弟优先于自己”作为判断标准。以下是两种修正思路:
思路一:直接判断无年长男性兄弟
% 男性继承人:君主的儿子,且没有更年长的男性兄弟 successor(X) :- male(X), child_of(X, A), throne(A), \+ (child_of(Y, A), male(Y), older(Y, X)). % 女性继承人:君主的女儿,且所有更年长的继承人都是女性(即没有年长的男性兄弟) successor(X) :- female(X), child_of(X, A), throne(A), \+ (child_of(Y, A), male(Y), older(Y, X)).
思路二:定义继承顺位优先级
% 定义继承顺位的优先级:男性优先于女性;同性别下年长优先 precedes(X, Y) :- male(X), female(Y). precedes(X, Y) :- male(X), male(Y), older(X, Y). precedes(X, Y) :- female(X), female(Y), older(X, Y). % 继承人是君主的子女中,没有其他子女比他/她优先级更高 successor(X) :- child_of(X, A), throne(A), \+ (child_of(Y, A), Y \= X, precedes(Y, X)).
执行修正后的代码,运行findall(X, successor(X), List).即可得到预期结果:List = [prince_charles, prince_andrew, prince_edward, princess_ann].
内容的提问来源于stack exchange,提问作者tryingThingsOut

