本系列前面的文章:

这是一道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 先生死在食品储藏室里,那里的凶器是煤气。

NMiniKanren解题

直接上代码:

var George = "George";
var John = "John";
var Rebert = "Rebert";
var Barbara = "Barbara";
var Christine = "Christine";
var Yolanda = "Yolanda";
var res = KRunner.Run(10, (k, q) =>
{
// 男人集合
var manNames = new string[] { George, John, Rebert };
var man = k.List(manNames);
// 女人集合
var womanNames = new string[] { Barbara, Christine, Yolanda };
var woman = k.List(womanNames);
// 所有人集合
var person = k.List(manNames.Concat(womanNames).ToArray());
// 每个场所所在的人
var bathroom = k.Fresh();
var dining = k.Fresh();
var kitchen = k.Fresh();
var livingroom = k.Fresh();
var pantry = k.Fresh();
var study = k.Fresh();
// 物品持有者
var bag = k.Fresh();
var firearm = k.Fresh();
var gas = k.Fresh();
var knife = k.Fresh();
var poison = k.Fresh();
var rope = k.Fresh();
// 不同的人在不同的房间
var locationConst = k.Distincto(bathroom, dining, kitchen, livingroom, pantry, study);
// 不同的人持有的物品不同
var weaponConst = k.Distincto(bag, firearm, gas, knife, poison, rope);
// 变量X表示凶手
var X = k.Fresh();
// 线索
// 厨房里面是一个男人,那里的凶器不是绳索、刀子、包和火枪。
var clue1 = k.All(
k.Is(kitchen, man),
k.Noto(k.Eq(kitchen, rope), k.Eq(kitchen, knife), k.Eq(kitchen, bag), k.Eq(kitchen, firearm)));
// Barbara 和 Yolanda 在浴室和书房。
var clue2 = k.Any(
k.All(k.Eq(bathroom, Barbara), k.Eq(study, Yolanda)),
k.All(k.Eq(bathroom, Yolanda), k.Eq(study, Barbara)));
// 带包的那个人不是 Barbara 和 George,也不在浴室和饭厅。
var clue3 = k.Noto(
k.Eq(bag, Barbara), k.Eq(bag, George),
k.Eq(bag, bathroom), k.Eq(bag, dining));
// 书房里面是一个带绳子的女人。
var clue4 = k.All(k.Is(rope, woman), k.Eq(rope, study));
// 起居室里面那件凶器,与 John 或 George 在一起。
var clue5 = k.Any(k.Eq(livingroom, John), k.Eq(livingroom, George));
// 刀子不在饭厅。
var clue6 = k.Noto(k.Eq(knife, dining));
// 书房和食品储藏室里面的凶器,没跟 Yolanda 在一起。
var clue7 = k.Noto(k.Eq(study, Yolanda), k.Eq(pantry, Yolanda));
// George 所在的那间屋子有火枪。
var clue8 = k.Eq(firearm, George);
// Boddy 先生死在食品储藏室里,那里的凶器是煤气。
var clue9 = k.All(k.Eq(X, pantry), k.Eq(X, gas));
// 集合所有条件
return k.All(
k.Is(X, person),
k.Is(bathroom, person),
k.Is(dining, person),
k.Is(kitchen, person),
clue5,
k.Is(livingroom, person),
k.Is(pantry, person),
k.Is(study, person),
clue2,
locationConst,
k.Is(bag, person),
k.Is(firearm, person),
clue8,
k.Is(gas, person),
k.Is(knife, person),
k.Is(poison, person),
k.Is(rope, person),
weaponConst,
clue1,
clue3,
clue4,
clue6,
clue7,
clue9,
k.Eq(q, k.List(
bathroom, dining, kitchen, livingroom, pantry, study,
bag, firearm, gas, knife, poison, rope,
X)));
});
Console.WriteLine("(bathroom dining kitchen livingroom pantry study bag firearm gas knife poison rope X)");
KRunner.PrintResult(res);

其中一些辅助函数:

k.Is(a, s): a是集合s的成员。

k.Noto(g1, g2, ...): g1g2……都不成立。NMiniKanren并没有支持“非”运算,这里用If方法模拟的,仅在一定场合下成立。

k.Distincto(a, b, c, ...): abc……两两不相等。

完整代码在https://github.com/sKabYY/NMiniKanren/blob/master/NMiniKaren.Tests/Crime.cs

另外,最后使用k.All整合所有条件时,并不是按照顺序写的。了解NMiniKanren运行原理后会知道,这是因为不同顺序会影响运行速度。大体上来说,应该尽可能让分支较少的放前面。

点击运行,等待几十秒,输出结果:

(bathroom dining kitchen livingroom pantry study bag firearm gas knife poison rope X)
[(Yolanda George Rebert John Christine Barbara John George Christine Yolanda Rebert Barbara Christine)]

凶手是Christine。

逻辑式编程语言极简实现(使用C#) - 2. 一道逻辑题:谁是凶手的更多相关文章

  1. Python 极简教程(十二)逻辑控制语句 if else

    计算机软件之所以能够对不同的情况进行不同的处理,就是我们在编码的时候,通过逻辑控制语句,告诉软件在不同的情况下应该做什么处理. 比如我们在登录的时候,那么当你输入正确的账号密码和错误的账号密码,完全是 ...

  2. Atitit.编程语言的主要的种类and趋势 逻辑式语言..函数式语言...命令式语言

    Atitit.编程语言的主要的种类and趋势 逻辑式语言..函数式语言...命令式语言 1. 编程语言的主要的种类 逻辑式语言..函数式语言...命令式语言 1 2. 逻辑式语言,,不必考虑实现过程而 ...

  3. 原生JS轮播-各种效果的极简实现

    寒假持续摸鱼中~此为老早以前博客的重写,当时还是分开写的,这里汇总重写,正好复习一遍~ 春招我来了! 所有有意思的,一股脑先扔进收藏,然后再也不看哈哈,真是糟糕. 今日事,今日毕,说起来容易. 当时竟 ...

  4. HTML5 极简的JS函数

    页面初始化 mui框架将很多功能配置都集中在mui.init方法中,要使用某项功能,只需要在mui.init方法中完成对应参数配置即可,目前支持在mui.init方法中配置的功能包括:创建子页面.关闭 ...

  5. 【股票盯盘软件】01_程序员炒股之开发一款极简风格的股票盯盘软件StockDog_V1.0.0.1

    1.前言 话说最近一段时间受疫情的影响,股市各种妖魔横行.本人作为一个入股市不满三年的小韭菜,就有幸见证了好几次历史,也是满心惊喜,就权当是接受资本市场的再教育了吧. 小韭菜的炒股方法其实很简单,这两 ...

  6. CSharpGL(28)得到高精度可定制字形贴图的极简方法

    CSharpGL(28)得到高精度可定制字形贴图的极简方法 回顾 以前我用SharpFont实现了解析TTF文件从而获取字形贴图的功能,并最终实现了用OpenGL渲染文字. 使用SharpFont,美 ...

  7. 有些ES6方法极简,但是性能不够好

    So,也许你觉得ES6让你视野大开,但是并不是性能也能跟得上~ 首先,让我们先来一个简单的性能测试: 数组去重 es5写法: function delSame(arr){ var n = []; ; ...

  8. 工具(5): 极简开发文档编写(How-to)

    缘起 一个合格的可维护项目,必须要有足够的文档,因此一个项目开发到一定阶段后需要适当的编写文档.项目的类型多种多样,有许多项目属于内部项目,例如一个内部的开发引擎,或者一个本身就是面向开发者的项目. ...

  9. 极简Python DeBug工具——PySnooper

    DeBug Python 代码的方式有很多种?比如: (1)设置断点 (2)print函数 (3)... 本文要介绍的是一个新开源的项目PySnooper ,只要给有疑问的代码加上装饰器,各种信息一目 ...

  10. .NET Core实战项目之CMS 第七章 设计篇-用户权限极简设计全过程

    写在前面 这篇我们对用户权限进行极简设计并保留其扩展性.首先很感谢大家的阅读,前面六章我带着大家快速入门了ASP.NET Core.ASP.NET Core的启动过程源码解析及配置文件的加载过程源码解 ...

随机推荐

  1. Blackfin DSP(三):BF533 的EBIU接口之flash

    上一节谈了GPIO问题,是用BF561 ezkit进行说明的,这是因为561 ezkit上的GPIO是与LED直连的,讲解GPIO时不会涉及到其它问题,降低了复杂性.对于533,也采取同样的操作即可. ...

  2. 【Python】使用 boto 调用 S3 对象存储API

    代码示例: import logging #from django.conf import settings import boto from boto.s3.key import Key impor ...

  3. PHP5.4最新特性

     PHP5.4最新特性   官网:ChangeLog-5.php#5.4.0 原文Oracle:LAMP 体系有了新的竞争,但此版本中的特性使 PHP 再次挑战极限. 稍微做了修改.: 概述总结:1. ...

  4. python拆分excel脚本

    因为需要将一个很大的excel按500条拆分为多个excel,手工操作实在太麻烦,就写了个python小脚本,现在是分为了多个sheet页,使用者可根据自己实际情况修改成多个文件的形式 #!/usr/ ...

  5. Qt5-控件-QMenu,QMenuBar-菜单栏详解-菜单热键-菜单校验功能

    #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QMenu> #inclu ...

  6. MVC VIEW 时间格式控制

    @Convert.ToDateTime(Model.CheckPatronExclusionResults.RequestTime).ToString("yyyy-MM-dd HH:mm:s ...

  7. Loading Image

    Android doesn’t handle animated gifs, but here’s one way to display an animated loading image that i ...

  8. javascript-无间缝滚动,封装

    原生javascript-无间缝滚动,封装 目前支持的是竖向与横向滚动 http://lgyweb.com/marScroll/ 现在分析下无间缝实现的基本思路(竖向例子): HTML结构: 1 &l ...

  9. 纯Css绘制三角形箭头三种方法

    在制作网页的过程中少不了绘制类似图片的三角形箭头效果,虽然工程量不大,但是确实麻烦.在学习的过程中,总结了以下三种方法,以及相关的例子. 一.三种绘制三角形箭头方法 1.方法一:利用overflow: ...

  10. 16. pt-mysql-summary

    pt-mysql-summary --host=192.168.100.101 --port=3306 --user=admin --password=admin \ pt-mysql-summary ...