使用逻辑式编程找出凶手!
Boddy 先生死于谋杀,现有六个嫌疑犯,每个人在不同的房间,每间房间各有一件可能的凶器,但不知道嫌疑犯、房间、凶器的对应关系。请根据条件和线索,找出谁是凶手。
本系列前面的文章:
逻辑式编程语言极简实现(使用C#) - 1. 逻辑式编程语言介绍
这是一道Prolog经典的练习题,中文翻译版来自阮一峰的文章《Prolog 语言入门教程》。
Boddy 先生死于谋杀,现有六个嫌疑犯,每个人在不同的房间,每间房间各有一件可能的凶器,但不知道嫌疑犯、房间、凶器的对应关系。请根据下面的条件和线索,找出谁是凶手。
六个嫌疑犯是三男(George、John、Robert)三女(Barbara、Christine、Yolanda)。
六个嫌疑犯分别待在六个房间:浴室(Bathroom)、饭厅(Dining Room)、厨房(Kitchen)、起居室(Living Room)、 储藏室(Pantry)、书房(Study)。每间房间都有一件可疑的物品,可以当作凶器:包(Bag)、火枪(Firearm)、煤气(Gas)、刀(Knife)、毒药(Poison)、绳索(Rope)。
所有线索如下:
线索一:厨房里面是一个男人,那里的凶器不是绳索、刀子、包和火枪。
线索二:Barbara 和 Yolanda 在浴室和书房。
线索三:带包的那个人不是 Barbara 和 George,也不在浴室和饭厅。
线索四:书房里面是一个带绳子的女人。
线索五:起居室里面那件凶器,与 John 或 George 在一起。
线索六:刀子不在饭厅。
线索七:书房和食品储藏室里面的凶器,没跟 Yolanda 在一起。
线索八:George 所在的那间屋子有火枪。
线索九:Boddy 先生死在食品储藏室里,那里的凶器是煤气。
直接上代码:
其中一些辅助函数:
<code>k.Is(a, s)</code>: <code>a</code>是集合<code>s</code>的成员。
<code>k.Noto(g1, g2, ...)</code>: <code>g1</code>、<code>g2</code>……都不成立。NMiniKanren并没有支持“非”运算,这里用<code>If</code>方法模拟的,仅在一定场合下成立。
<code>k.Distincto(a, b, c, ...)</code>: <code>a</code>、<code>b</code>、<code>c</code>……两两不相等。
完整代码在https://github.com/sKabYY/NMiniKanren/blob/master/NMiniKaren.Tests/Crime.cs
另外,最后使用<code>k.All</code>整合所有条件时,并不是按照顺序写的。了解NMiniKanren运行原理后会知道,这是因为不同顺序会影响运行速度。大体上来说,应该尽可能让分支较少的放前面。
点击运行,等待几十秒,输出结果:
凶手是Christine。