逆波兰表达式在详单查询中的应用

应用场景
用户的原始详单存放在Hbase中,每一行的内容如下所示:用“^|”按字段分隔
vc^|01^|460023451018866^|013044003610490^|15804620200^|1008611^|20160215181103^|10^|^|8613441707^|4573^|5191^|218e1^|60001^|HDS01O^|HBSM2TI^|000^|^|0^|a^|c^|000^|451^|451^|451^|451^|451^|9^|0^|0^|000^|020^|0^|^|0^|^|0^|0^|0^|^|0^|HRM81710^|160215181230^|1^|00000^|00^|0000000^|000000000^|00000^|0000000^|000000000^|000000000000000^|hn^|0^|^|^|1580462^|16^|02^|15^|451^|^|^|0^|0^|0^|218e160001^|00^|00000000^|00000000^|^|0^|^|20160215181500^|^|^|^|^|
详单查询的时候需要从众多的详单记录中筛选出需要的记录,而查询场景是变化的,就需要根据场景将条件配置起来
举个简单的例子:根据通话开始结束时间筛选vc的话单
head_info:手机号码|通话开始开始时间|通话时长|通话小区(lac+cell_id)
sort_col:1
col_disp_type:C|D11|C|C|C
begin:1
  record_type:vc**
  base_col_info:4|6|7|10|11
  row_info:([00=vc]&[06>=$1]&[06<=$2])
end:1
具体解析的方式这块不讲,上面标红的部分是这个场景下解析需要的条件
条件的业务含义:00表示第0位,第0位等于vc的记录;&表示与(自定义的,和其他语言的不一样,|表示或);$1表示传入查询的开始时间,$2表示传入查询的结束时间
程序需要将上面的运算表达式解析装入内存,用于快速匹配校验,就需要一种算法:逆波兰表达式

算法原理介绍
表达式一般由操作数(Operand)、运算符(Operator)组成,例如算术表达式中,通常把运算符放在两个操作数的中间,
这称为中缀表达式(Infix Expression),如A+B。
波兰数学家Jan Lukasiewicz提出了另一种数学表示法,它有两种表示形式:
把运算符写在操作数之前,称为波兰表达式(Polish Expression)或前缀表达式(Prefix Expression),如+AB;
把运算符写在操作数之后,称为逆波兰表达式(Reverse Polish Expression)或后缀表达式(Suffix Expression),如AB+;
其中,逆波兰表达式在编译技术中有着普遍的应用。
算法:
一、 将中缀表达式转换成后缀表达式算法:
1、从左至右扫描一中缀表达式。
2、若读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈
3、若读取的是运算符
  (1) 该运算符为左括号"(",则直接存入运算符堆栈。
  (2) 该运算符为右括号")",则输出运算符堆栈中的运算符到操作数堆栈,直到遇到左括号为止。
  (3) 该运算符为非括号运算符:
      (a) 若运算符堆栈栈顶的运算符为括号,则直接存入运算符堆栈。
      (b) 若比运算符堆栈栈顶的运算符优先级高或相等,则直接存入运算符堆栈。
      (c) 若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。
4、当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。
二、逆波兰表达式求值算法:
1、循环扫描语法单元的项目。
2、如果扫描的项目是操作数,则将其压入操作数堆栈,并扫描下一个项目。
3、如果扫描的项目是一个二元运算符,则对栈的顶上两个操作数执行该运算。
4、如果扫描的项目是一个一元运算符,则对栈的最顶上操作数执行该运算。
5、将运算结果重新压入堆栈。
6、重复步骤2-5,堆栈中即为结果值。

算法实践
[00=vc]&[06>=$1]&[06<=$2] 将此中序表达式转换为后缀表达式:
00 vc = 06 $1 >= & 06 $2 <= &
具体过程见附件代码:
        PolishNotation polishNotation = new PolishNotation();
        List filters = polishNotation.parse(polishNotation.trans("[00=vc]&[06>=$1]&[06<=$2]"));
放入内存,在解析详单的过程中,只需要将原始详单行、入参、查询条件的后缀表达式传入match方法,就可以返回是否匹配的结果,解析效率非常高效
    /**
     * 根据话单行、查询入参和过滤条件计算是否匹配
     * @param line 原始详单行数组
     * @param args 查询入参
     * @param filters 条件后缀表达式
     */
    public boolean match(String[] line, List args, List filters)
不知道大家是否看懂了吗,欢迎讨论

你可能感兴趣的:(算法)