推荐语雀观看视角,让文章体验更好
推荐语雀观看视角,让文章体验更好
推荐语雀观看视角,让文章体验更好
应对及其复杂的表达式计算,善于对语法进行分析拆解重组。
当我们需要实现一个加减乘除的计算方法之后,你会如何去做?
比如 : **3+2*6+5-2*2-1-1-1-1-1-1-1-1+2/2=?**
- 乘法和除法是需要先计算结果的。
- 加法和减法需要从左到右依次处理
- 最后得到结果
我们先来看下Spring
中的解释器使用方式:
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
public class ExpressionTest {
@Test
public void test(){
ExpressionParser parser = new SpelExpressionParser();
Expression expression = parser.parseExpression("3+2*6+5-2*2-1-1-1-1-1-1-1-1+2/2");
int result = (Integer) expression.getValue();
System.out.println("计算结果是:" + result);
// 计算结果是:9
}
}
如何设计?
- +、-、*、/ 都是计算符号,需要独立识别并且计算。
- 数字值库理解为字面值,需要和计算符号拆分开。
得到上面的信息我们可以这么去设计:
package com.lkx.code.utils;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class JieShiQi {
/**
* 最上层表达式
*/
public interface Expression {
int interpreter(Context context);//一定会有解释方法
}
/**
* 非终结符 , 可以理解为定义的符号比如 + 、 - 、 * 、/
*/
public static abstract class NonTerminalExpression implements Expression {
Expression e1, e2;
public NonTerminalExpression(Expression e1, Expression e2) {
this.e1 = e1;
this.e2 = e2;
}
}
/**
* 减法
*/
public static class MinusOperation extends NonTerminalExpression {
public MinusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//将两个表达式相减
@Override
public int interpreter(Context context) {
int left = this.e1.interpreter(context);
int right = this.e2.interpreter(context);
int i = left - right;
System.out.println("- : " + left + " - " + right + " = " + i);
return i;
}
}
/**
* 乘法操作器
*/
public static class MultiplyOperation extends NonTerminalExpression {
public MultiplyOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//将两个表达式相减
@Override
public int interpreter(Context context) {
int left = this.e1.interpreter(context);
int right = this.e2.interpreter(context);
int i = left * right;
System.out.println("* : " + left + " * " + right + " = " + i);
return i;
}
}
/**
* 终结符号 : 可以理解为字面量 比如 1 + 2 其中1和2代表的就是字面量
*/
public static class TerminalExpression implements Expression {
String variable;
public TerminalExpression(String variable) {
this.variable = variable;
}
@Override
public int interpreter(Context context) {
//因为要兼容之前的版本
Integer lookup = context.lookup(variable);
if (lookup == null)
//若在map中能找到对应的数则返回
return Integer.valueOf(variable);
//找不到则直接返回(认为输入的就是数字)
return lookup;
}
}
public static class PlusOperation extends NonTerminalExpression {
public PlusOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//将两个表达式相加
@Override
public int interpreter(Context context) {
int left = this.e1.interpreter(context);
int right = this.e2.interpreter(context);
int i = left + right;
System.out.println("+ : " + left + " + " + right + " = " + i);
return i;
}
}
public static class DivisionOperation extends NonTerminalExpression {
public DivisionOperation(Expression e1, Expression e2) {
super(e1, e2);
}
//将两个表达式相加
@Override
public int interpreter(Context context) {
int left = this.e1.interpreter(context);
int right = this.e2.interpreter(context);
int i = left / right;
System.out.println("/ : " + left + " / " + right + " = " + i);
return i;
}
}
/**
* 上下文操作 涵盖 可见变量、操作关系、还新增解析器等等
*/
public static class Context {
private Map<String, Integer> visibleValueMap = new HashMap<>();
private Map<Character, Class> operationMap = new HashMap<>();
public void registerOperation(Character character, Class<? extends NonTerminalExpression> clazz) {
operationMap.put(character, clazz);
}
public boolean isOperation(Character character) {
return operationMap.containsKey(character);
}
//定义变量
public void add(String key, Integer value) {
visibleValueMap.put(key, value);
}
//将变量转换成数字
public Integer lookup(String key) {
return visibleValueMap.get(key);
}
private Object getExpressionObject(char c, Expression left, Expression right) {
try {
return operationMap.get(c).getConstructor(Expression.class, Expression.class).newInstance(left, right);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
/**
* 解释器组件,负责解释字面量并构建成一个树叶节点的链表对象
*/
public static class InterpreterComponents {
private String sourceString;
private int currentIndex;
private char[] charArray;
private int sourceLength;
Stack<Expression> objectStack = new Stack<>();
public InterpreterComponents(String str) {
this.sourceString = str;
this.charArray = str.toCharArray();
this.sourceLength = str.length();
this.currentIndex = 0;
}
public Expression builder(Context context) {
Expression left = sum(context);
return left;
}
public Expression sum(Context context) {
Expression left = cheng(context);
while (isPeekToken('+', '-')) {
Character character = isPeekToken();
nextToken();
Expression regiht = cheng(context);
left = (Expression) context.getExpressionObject(character, left, regiht);
}
return left;
}
public Expression cheng(Context context) {
Expression left = text(context);
while (isPeekToken('*', '/')) {
Character character = isPeekToken();
nextToken();
Expression regiht = text(context);
left = (Expression) context.getExpressionObject(character, left, regiht);
}
return left;
}
/**
* 获取文本值,也就是字面值,而非符号。
*
* @param context
* @return
*/
public Expression text(Context context) {
Character text = isPeekToken();
StringBuilder textBuilder = new StringBuilder();
while (text != null && !context.isOperation(text)) {
textBuilder.append(text);
if (nextToken() != null) {
text = isPeekToken();
} else {
break;
}
}
return new TerminalExpression(textBuilder.toString());
}
/**
* 判断当前下标是否涵盖特定符号
*
* @param ch
* @return
*/
private boolean isPeekToken(Character... ch) {
Character t = isPeekToken();
if (t == null) {
return false;
}
for (int i = ch.length - 1; i >= 0; i--) {
if (t == ch[i]) {
return true;
}
}
return false;
}
/**
* 获取当前下标的值
*
* @return
*/
public Character isPeekToken() {
if (currentIndex == sourceLength) {
return null;
}
return charArray[currentIndex];
}
/**
* 指针向下移动
*
* @return
*/
public Character nextToken() {
if (currentIndex == sourceLength) {
return null;
}
return charArray[currentIndex++];
}
public boolean isEnd() {
return currentIndex == sourceLength;
}
}
public static void main(String[] args) {
String str = "3+2*6+5-2*2-1-1-1-1-1-1-1-1+20/2+${abc}";
Context context = new Context();
// 注册非终结符号的操作类
context.registerOperation('+', PlusOperation.class);
context.registerOperation('-', MinusOperation.class);
context.registerOperation('*', MultiplyOperation.class);
context.registerOperation('/', DivisionOperation.class);
// 可见值,也可以理解为变量字段名称。
context.add("${abc}", 110);
// 解释器,将str的每一个符号进行解释并计算得到一个链表节点,里面涵盖了执行顺序
InterpreterComponents interpreterComponents = new InterpreterComponents(str);
Expression builder = interpreterComponents.builder(context);
// 这里会进行递归获取结果
int interpreter = builder.interpreter(context);
System.out.println(interpreter);
}
}
结果:
3+2*6+5-2*2-1-1-1-1-1-1-1-1+20/2+${abc} = ?
* : 2 * 6 = 12
+ : 3 + 12 = 15
+ : 15 + 5 = 20
* : 2 * 2 = 4
- : 20 - 4 = 16
- : 16 - 1 = 15
- : 15 - 1 = 14
- : 14 - 1 = 13
- : 13 - 1 = 12
- : 12 - 1 = 11
- : 11 - 1 = 10
- : 10 - 1 = 9
- : 9 - 1 = 8
/ : 20 / 2 = 10
+ : 8 + 10 = 18
+ : 18 + 110 = 128
--> 最终结果: 128
大体类图:
上面的实现思路都是基于Spring中的SpelExpressionParser来做的,不过没有全部实现,仅仅实现部分,更为复杂的比如()等等,不过基于以上思路可以继续延伸。
Spring中的解释器执行顺序:
由上往下优先级从低到高
org.springframework.expression.spel.standard.InternalSpelExpressionParser#eatExpression | |||
---|---|---|---|
方法名称 | 处理符号 | 描述 | |
eatExpression | =、?:、? | ||
eatLogicalOrExpression | or、** | ||
eatLogicalAndExpression | and、&& | ||
eatRelationalExpression | GT、LT、LE、GE、EQ、>、<、<=、>=、!=、instanceof、matches、between | ||
eatSumExpression | +、-、++ | ||
eatProductExpression | *、/、% | ||
eatPowerIncDecExpression | ^、++、– | ||
eatUnaryExpression | +、-、!、++、– | ||
eatPrimaryExpression | eatNode | .、?.、[、] | |
eatStartNode> 这里面的方法可以理解为将一些特殊符号进行替换,或者提前解释比如()、[]、{}等等,都是要先将括号内的表达式先处理。例如@代表Spring中的标记,先提前转换。 |
- PropertyOrFieldReference
- MethodReference
- VariableReference
- FunctionReference
- Projection
- Selection
- Indexer
相关Spring的Expression的备忘录
版权声明:本文为lkx444368875原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。