逻辑式编程还有用吗?–“三维度”逻辑编程语言的设计(2)

1,逻辑编程语言能做什么   

    这两天正在构思这个“三维度”逻辑编程语言的设计系列的下一篇该怎么写,正好在上一篇《用写文章的方式写程序–“三维度”逻辑编程语言的设计(1)》有位叫做 dwcz 的朋友回帖说:

    “没啥特点。新出的语言都快实现的功能,还在这里进行理论构想。逻辑式编程基本被否定了,和函数式有同样问题–只能在简单或静态环境,在复杂或动态环境,造成的问题比要解决的问题还多。”

    不知道这位朋友具体是出于什么原因这么认为的,我认为一个东西不流行不代表它是失败的、被否定的事物。逻辑编程语言这几年的确很少出现在编程社区的讨论中,更看不到有关职位的招聘信息,本着“用脚投票”的原则,认为逻辑编程语言没什么用,已经被否定、被淘汰的观点就有市场了,但是这种观点是不正确的,流行的东西不一定是好东西,比如流感;不流行的东西只不过很小众,只要它存在就有它的一小片天地。逻辑编程语言的确很小众,它有它特殊的应用领域, dwcz 恰恰说反了,逻辑编程语言主要的用武之地就是复杂和动态的环境,这个可以从逻辑编程语言Visual Prolog的官网介绍看到:

//www.visual-prolog.com/default-chinese.htm
使用Visual Prolog可以构建工业级的商用程序,尤其适合处理复杂的认知问题。
Prolog语言开发中心业已证实,以下项目中的先进资源调度和决策支持系统完全是用Visual Prolog编写的:

员工规划
机场决策支持
航空公司决策支持
车间调度
基于语音的解决方案
CrewWatch
Ra

 上面一段介绍的详细内容请点击上面的链接了解。而函数式语言跟逻辑编程语言有同样问题这个说法更不对,函数式语言的鼻祖LISP至今还在,基于JVM的LISP方言Clojure还能常常出现在招聘信息中,还有比Common LISP更为简单的方言Scheme,它有一个完善IDE环境的DrRacket实现,还有编译和运行速度比C还快的Chez Scheme,详情可以看看知乎对此的讨论,也可以看看王垠写的这篇《揭秘Chez Scheme》(王垠自己的博客已经加密,这里贴的是转载的文章)。我使用Chez Scheme编写了一个新冠病毒感染风险监测程序,可以点击我这篇文章《Scheme语言实例入门–怎样写一个“新型冠状病毒感染风险检测程序” 》了解和下载运行这个Scheme程序。此外,常用的函数式语言还有scala,  erlang, F#, Haskell等,说到.NET就不能不提到F#,.NET 5.0将伴随C#9.0同步发布最新的F# 5.0,可见F#在软微开发语言中的重要地位。奇怪的是,F#在国内鲜有使用,而在国外被称为薪水最高的编程语言,如下图2019编程语言薪资排行榜

编程语言薪水排行榜

 

    从上面的编程语言排行榜上可以看到,Top 3的语言有两名都是函数式语言,Erlang都能排名No.9,可见,函数式语言是名副其实的“高薪语言”,还能说函数式语言“只能在简单或静态环境”,而不能运用在复杂或动态环境的环境吗?没有哪个老板会在那种“简单或者静态环境”的软件开发项目中给程序员高新吧?回归主题,本篇文章是讨论逻辑编程语言的,上面说这么多,就是要告诉大家,逻辑式编程的重要性,它尤其适合处理复杂的认知问题,解决复杂的业务问题,同样能够构建工业级的商业软件

2,从一个游戏认识逻辑编程

    前面说了逻辑编程语言的重要性,简单介绍了Visual Prolog这个逻辑编程语言的用途,但对于习惯了“命令式编程”的程序员来说可能对于逻辑编程语言还是没有概念。

    Prolog 语言是以一阶谓词逻辑演算为原理设计的计算机程序语言,在人工智能的发展 历程中被寄予厚望,曾经被成为“第五代计算机语言”。Prolog 的程序结构就是事实、规则 和问题,它内置一个推理机,通过输入事实,处理规则,求解问题。因此它跟其它程序语言 都不同,大部分都是命令式的,而 Prolog 是陈述式的,因此不需要告诉 Prolog 程序的执行 顺序即可求解问题。

    比如看下面的Prolog程序例子:

likes(bell, sports).     % Bell 喜欢运动
likes(mary, music).      % Mary 喜欢音乐
likes(mary, sports).     % Mary 喜欢运动
likes(jane, reading).    % Jane 喜欢阅读
friend(john, X) :- likes(X, reading), likes(X, music).   % 成为 john 的朋友需要喜欢 阅读和音乐
friend(john, X) :- likes(X, sports), likes(X, music).    % 成为 john 的朋友需要喜欢 运动和音乐 
 
?- friend(john, Y).      %谁是 john 的朋友? 

    运行此程序,将获得答案:

Y= mary

    上面的代码第1-第4行,都在说关于“喜欢”什么的事实;第5、第6行,分部定义了成为某个人的朋友的条件,这些条件成为一套规则;最后一行代码是提问。运行Prolog程序后,Prolog内置的“推理机”回溯程序定义的事实,匹配定义的规则,将问题代入这些事实和规则进行消解,最后匹配出答案。(有关这个过程的理解,推荐大家看看《逻辑式编程语言极简实现(使用C#) – 1. 逻辑式编程语言介绍》这篇文章,作者写得诙谐有趣,浅显易懂。)

    上面有关Prolog的介绍节选自本人的新书《SOD框架“企业级”应用数据架构实战》【2.1.6 数据、信息和知识】一小节的《第三,什么是知识(Knowledge)?》的内容,已购书的朋友可以看看书中相关内容更多的介绍。

    也许上面这个示例程序的运行结果有点”费脑子“,并且这个程序是原生的Prolog程序,不喜欢这种语法风格。没关系,我们只要明白Prolog语言就是由事实、规则和问题组成的就行了。下面我们再通过一个简单一点的“游戏人生”程序来带领大家认识逻辑编程,并且示例代码使用大家熟悉的C#语言来模拟Prolog程序的风格,这样来看逻辑编程就会自然点。编程之前,我们都会做需求分析,产品经理会给我们讲一个”用户故事“,在这个游戏中这是两口子决策是否要生孩子的故事。

故事内容:

  1. 有一个姑娘很漂亮,美如貂蝉;
  2. 有一个小伙年轻有为,名叫张三;
  3. 张三是一个打工仔;
  4. 貂蝉是张三的妻子;
  5. 张三是貂蝉的丈夫;
  6. 貂蝉还没有生孩子;
  7. 丈夫可以努力工作打工赚钱;
  8. 妻子过了35岁生孩子就晚了;
  9. 丈夫要孩子不能超过60岁,且存款不能小于1万元;
  10. 张三努力打工。
  11. 张三和貂蝉现在可以生孩子了吗?

   在这个故事中,第1条-第6条,以及第10条叙述的是故事男女主角已有的“事实”,第7-9条定义的是家庭中有关生孩子的“规则”,第11条提出问题。事实一经发生就不可改变,事实可以是一些对象之间的关系,也可以是对象的行为,比如这里说貂蝉是张三的妻子。规则是一些强制性约束,比如社会性的、生理性的或者法律上的,一般也不可以随意改变。这里定义的规则只有合法成为夫妻才可以生孩子,所以需要先描述男女主角是夫妻关系。当然不结婚也可以生孩子,但这不是本程序考虑的规则。根据事实和规则,我们就能回答一些问题了,这里的问题是男女主角何时能够生孩子。

    下面,用C#代码来表示这个故事有关的事实、规则和问题:

            Woman diaochan = new Woman() { Name = "貂蝉", Birthday = new DateTime(1990, 1, 2) };
            Man zhangsan = new Man() { Name = "张三", Birthday = new DateTime(1988, 3, 5) };
            
            Worker worker1 = new Worker(zhangsan);
            Wife wife1 = new Wife(diaochan,zhangsan);
            Husband husband1 = new Husband(zhangsan,diaochan);

            diaochan.ChildrenCount = 0;
            diaochan.ActAs<Wife>().Child_bearing();   //生孩子
            zhangsan.ActAs<Husband>().Money += zhangsan.ActAs<Worker>().Work();
            zhangsan.ActAs<Husband>().Child_rearing();  //生孩子

    请看上面这个代码,基本上和我们的故事“剧本”描述的一模一样,只不过,生孩子是妻子和丈夫两个人的事情,“一头熟”可生不下孩子,所以对象diaocan和zhangsan都可以调用生孩子的方法Child_rearing() ,但是他们两个人真的能生孩子吗?这里不得不提出一个严肃而认真的问题:生孩子不是小事,它要看情况。这个“看情况”讲的就是一个环境、时机、条件等等,比如是否符合我国有特色的“计划生育制度”,是否符合优生优育,物质条件是否足够,心理有没有做好准备。。。在本文中,我将这种“看情况”有一个正式的词语来表达–场景,在当前的游戏人生故事中,这里的问题就是“生育场景”中的问题。

3,“三维度”逻辑编程

    在“三维度”理论中,场景就是有角色参与的,角色在其中进行交互活动的环境。场景因为有角色参与才有意义,角色因为有场景的存在才能发挥角色的行为。在角色与场景的交互过程中,角色和场景的改变可能会诞生新的角色、产生新的场景,而这种变化可以体现在时间维度上。所以三维度理论中的角色、场景和时间是相互影响、紧密相关、不可或缺的关系,具体内容可以参考我之前的文章《业务分析三维度(场景+角色+时 间)之程序员坐禅论道》。用三维度理论可以可以很好的描述我们这个游戏人生故事中的生孩子问题,它的角色维度正好可以描述逻辑编程语言中“一阶谓词”,比如上面代码中的Worker、Wife、Husband类,这些“谓词”描述了对象的特征,或者对象之间的关系,可以表达一些“事实”之间的关系,实现逻辑编程语言中的“谓词演算”;它的场景维度可以用来构建一组相关的事实,并且表达这些事实相互之间的一套规则,也就是场景规则。场景更像一个剧本中的槽,这是专家系统中有关知识表达的高级话题,在此先不予讨论。

    所以,要解决当前游戏中男女主角是否能生孩子的问题,我们的程序还必须引出“场景”对象,定义一个生育场景,它包括一套有关生孩子的规则,这个规则不同于丈夫、妻子角色自身的规则,前者是社会性、法律法规性质的约束;后者是生理性、心理性的约束。比如我国之前的计划生育制度,要求一对夫妻只能生育一个孩子;现在的制度是可以生两个。因此,场景规则随着时间的推移也可以是可以改变的,脱离时间维度去看场景问题是不对的,同样脱离时间维度去看角色问题也不对,比如我们的故事中男女主角都会随着时间的变化而增大年龄,有可能过了最佳生育年龄。这里可以总结出,角色对象有角色固有的规则,场景对象也有场景的规则。

    假定我们已经定义了一个生育场景对象,我们就可以将男女角色放入生育场景,开始我们故事的排练了,而这个就是我们故事要写的剧本。为了简化剧本,下面的代码不再重复前面已经定义的事实,直接简化为下面的过程:

  1. 进入场景。
  2. 生孩子?
  3. 努力工作赚钱。
  4. 生孩子?
  5. 结束场景。

对应的代码如下:

 

 Console.WriteLine("-----开启【生育场景】规则测试-------------");
            diaochan.ChildrenCount = 0;
            husband1.Money = 5000;
            ProcreateContext context = new ProcreateContext(new DateTime(2010, 1, 1), zhangsan, diaochan);
            context.StartContext();
            //场景参与人开始扮演角色
            diaochan.ActAs<Wife>().Child_bearing();
            zhangsan.ActAs<Husband>().Money += zhangsan.ActAs<Worker>().Work();
            zhangsan.ActAs<Husband>().Child_rearing();
            //启动规则匹配
            Console.WriteLine();
            bool rulesFlag = context.MatchRules();
            Console.WriteLine("{0} 结果:{1}", context.Name, rulesFlag);
            Console.Read();

 

    运行这段程序,即可看到张三和貂蝉是否可以生孩子的结果,具体运行结果将在本系列结束后公布,读者也可以从我的《SOD框架“企业级”应用数据架构实战》一书中事先看到答案。

4,本篇小节

    本篇先讨论了什么是逻辑编程,以及逻辑编程的重要性,然后用一个实例介绍了Prolog这门逻辑编程语言。然后思考逻辑编程的特点,它和我们的“三维度”理论有着天然的契合度。运用三维度理论,我们可以很容易的用一种非逻辑编程语言–C#来实现逻辑编程的范式,这样我们就能结合逻辑编程的有点以及.NET平台语言强大的功能,从而轻松的构建一个新的逻辑编程语言,尽管这只能称之为一种DSL,但它也能为一种新的逻辑编程语言的设计提供一个可实现的参考方案。

    在下一篇,我们将讨论这个“三维度”逻辑编程语言的设计细节,已经购买了我的书的朋友可以先一睹为快。大家有什么问题可以回帖留言,也方便为我下一篇具体写作内容提供思路,谢谢大家的支持。