这是我参与「第五届青训营」伴学笔记创作活动的第 7 天。
今天,我主要学习了规则引擎的相关知识。
一、规则引擎的引入
如果我们是一个商城管理系统的程序设计人员,那么我们往往会在业务逻辑代码中涉及到购买商品优惠的计算,例如满300减20、满1000减100、满2000打8折、买二赠一等等,他们种类繁多、计算麻烦、优先级模糊不清、且频繁变动,可以说是程序员的噩梦了。尽管我们可以通过合适的设计方式(例如采用策略模式)来减少其对代码的影响,但是无论怎么说,这都需要大批程序员在代码中处理繁杂的逻辑,并不是一种很“和谐”的开发方式。
那么,人类理解这些“优惠方式”的速度往往是优于计算机的,那么我们能不能让计算机像人类一样,自动读取这些“条件”,随后分析出应该支付多少钱呢?也许你会认为这是神经网络、自然语言处理之类的活(他们确实也能做),但是实际上我们有一种更加合理的方案:
如果我们让这些规则以一定的形式陈列出来,随后让程序将其规范化,随后将金额(以及其他的数据,例如购买量)输入,得到一个结果,这个流程像什么?难道不很像源代码被编译成程序后被执行的场景吗?(规则就是源代码,规范化就是编译成二进制文件,传入参数并得到结果就相当于运行)
这种方式对于业务人员和开发人员是双赢:业务人员能够直接编写规则,不必担心开发人员误解了意图;开发人员无需反复改动代码,大幅减少了开发流程。
二、编译原理基础入门
规则引擎在本质上就是一个简化版本的编译器(解释器),因此我们需要接触并学习一些编译原理的基础知识:
2.1 词法分析
在编译(解释)的第一个阶段,编译器会从左到右读取源代码字符,识别出各个单词(定义:具有理解意义,即能够被理解的最小语法单位),并确定单词的类型(常量,变量,比较符,关键字等等),并将识别到的单词转换成统一的表示形式,俗称词法单元(token)。
为了实现这一目标,我们往往需要使用有限自动机(永远的痛,打了四年多算法竞赛,字符串算法还是只会哈希)。
2.2 语法分析
将代码分割成若干单元后,我们需要识别出“短语”,构造出一棵语法分析树。
为了实现这一目标,我们往往需要使用推导和归纳(这就涉及离散数学里面学到的数理逻辑了)。
整个语法分析过程可以被可视化为一棵树(不一定是二叉树),需要使用特定的数据结构来存储。
2.3 语义分析
对于一部分代码,他们只需要直接执行就可以了,不需要过分的语义分析:
printf("Hello world!"); // 执行就完事了
但是,有一些代码则不是这样,他需要额外的理解(甚至需要考虑上下文):
int a = b + c; // b, c是啥?我之前处理过吗?他们值是多少?
free(p); // 有这地址吗?我咋不记得我以前定义过?
int a = a + 3; // 奶奶的,给我玩阴的是吧
对于此,我们需要语义分析功能来存储变量及其作用域,记录上下文信息,检查是否出现了上下不一致的现象。
一般来说,到了这一步,编译器一般会产生一些中间文件(因为分析部分基本上结束了),以供下面的操作。
2.4 二进制文件生成
得到了中间文件后,编译器就需要根据机器的特殊性,针对性的翻译出合适的汇编代码了(解释器就直接用内部的虚拟机执行即可)。