(给DotNet加星标,提升.Net技能)
转自:古霜卡比 cnblogs.com/skabyy/p/13209724.html
本系列前面的文章:
《逻辑式编程语言极简实现(使用C#):逻辑式编程语言介绍》
讲NMiniKanren的运行原理。老明敲了敲白板,开始涂画代码,我们从一个喜闻乐见的例子开始。
KRunner.PrintResult(KRunner.Run(null, (k, q) =>
{
var x = k.Fresh();
var y = k.Fresh();
return k.All(
k.Any(k.Eq(x, 1), k.Eq(x, 2)),
k.Any(k.Eq(y, x), k.Eq(y, "b")),
k.Eq(q, k.List(x, y)));
}));
这题我会了!小皮在例子下边写下答案:
[(1 1), (1 b), (2 2), (2 b)]
看到小皮没把昨天的知识忘光,老明略感欣慰:不错。你这个答案是怎么算出来的呢?
呃……就是那个……小皮忽然卡壳了。这种问题就好比几何证明题,明明一眼就能看出来的两条垂直线,真下手证明却发现还挺不容易。小皮抓了几把头发,总算理出一缕思绪:大概就是找出所有条件可能的组合……然后算一下解……小皮一边说,一边在白板上写着:
嗯,其实你已经知道怎么算出答案来了。只是对于其中的细节还不甚明了。我们接下来要做的事要理清楚这个计算过程,得到一个每一步都可以由计算机明确执行的算法。
这个算法其实就是你所说这样,找出所有可能的条件组合。每组条件组合可以求出一个解,也可能自相矛盾从而无解。由于NMiniKanren中的条件都是相等条件,所以一组条件组合可以看作一个替换(Substitution)。一个替换能产生一个解,或者无解。
因此,只需解决下面两个问题:
要在什么数据结构上按照什么顺序遍历替换。
如何从替换中算出一个解,或者判断其无解。
遍历分支
首先,我们要从代码构造出一个数据结构(其实就是一张图)。这个数据结构能够按照一定的顺序进行遍历,并依次生成替换。
例子