这个解决方案使用chr
库,它实际上是一种与 Prolog 不同的语言。我现在自己在学习 CHR,这实际上是我用它编写的第二个程序。它可能与我在邮件列表中获得的改进版本过于相似。所以带着一粒盐吃。
在我看来,CHR 似乎非常适合从很多可能性开始然后消除不良可能性的程序。也许取消可以继续,直到你收敛到一个解决方案。这就是这个程序的工作方式,我们生成所有可能的姓名/姓氏集合,然后使用我们的不可能约束来减少可能的姓氏集合,直到它们收敛到一个,然后我们将它们从其他姓名/姓氏集合中删除,直到一切收敛。
前两行介绍了我们将使用的库和约束:
:- use_module(library(chr)).
:- chr_constraint name/1, surnames/1, impossible/2, matched/2, possible/2.
第一个约束产生了我们需要走过的可能性。
gen_possibilities @
name(Name), surnames(Surnames)
==> possible(Name, Surnames).
这里的想法是我们已经将姓氏放在一个列表中,我们只是possible
在每个名字和可能的姓氏总数之间创建一对。
impossibility @
impossible(Name, Surname), possible(Name, Surnames)
<=> select(Surname, Surnames, Remaining)
| possible(Name, Remaining).
一旦我们被告知某事是不可能的,我们可以简单地将其从该名字的可能姓氏列表中删除。select/3
对于从列表中删除单个项目并为您提供剩余部分很有用。如果姓氏已经不在可能的姓氏列表中,我们在此处使用警卫,这可能在应用第四个约束之后发生。
only_possiblity @
possible(Name, [Surname])
<=> matched(Name, Surname).
一旦可能性列表减少到一个名字,我们就成功地进行了匹配。
remove_matched @
matched(_, Surname), possible(Name, _)
==> impossible(Name, Surname).
如果我们进行了匹配,那么对于所有其他名称,该姓氏将变得不可能。
如果由于名称已被处理而无法吸收不可能的约束,则可以添加额外的清理约束,但不是必需的:
irrelevant_impossibility @
possible(Name, Surnames)
\ impossible(Name, Surname)
<=> \+ memberchk(Surname, Surnames)
| true.
现在我们可以运行程序了。我冒昧地为 5 年级/6 年级和不同的父亲内联逻辑。我觉得这个意图很容易通过阅读来弄清楚,我认为通过专门建模它不会有太多收获。感谢@RobertBaron 在他的解决方案中遇到了麻烦!
main :-
name(misha),
name(vova),
name(petya),
surnames([ivanov, semyonov, gerasimov]),
impossible(misha, gerasimov), % stated impossible
impossible(vova, gerasimov), % different grades
impossible(vova, ivanov). % different fathers
执行此操作的结果是:
?- main.
name(petya),
name(vova),
name(misha),
surnames([ivanov, semyonov, gerasimov]),
matched(petya, gerasimov),
matched(misha, ivanov),
matched(vova, semyonov).
我相信这满足了摆在您面前的限制条件。