第四篇 生成对象树 第十四章 - 解释器模式

  对象树不仅可以表示对象在结构上的组成关系,还可以表示对象在逻辑上的运算关系。
  下面以四则运算代数式的求值过程为例,考察对象树的作用。
  给出任意四则运算代数式,比如

        a * (b + c) + d   (式一)

  简便起见,先把它转换成逆波兰表达式(Reverse Polish Notation,RPN)。逆波兰表达式可以帮我们解决算符的优先级问题(() >* >+)。(式一)转换后变成

        a b c + * d +   (式二)

  可以使用下面的简易工具进行转换。

// 代数式工具
public class ExpressionUtil {

  // 算符列表
  private static List<String> OPS = Arrays.asList("+", "-", "*", "/", "(", ")");

  // 将四则运算代数式转换成逆波兰表达式
  public static List<String> convertToRPN(String expression) {
    List<String> expArr = split(expression);
    List<String> result = new ArrayList<>();
    Stack<Operator> s = new Stack<>();
    for (String exp : expArr) {
      if (!OPS.contains(exp)) {
        result.add(exp);
        continue;
      }
      switch (exp) {
      case "+":
      case "-":
          while (!s.isEmpty() && s.peek().level >= 1) {
            result.add(s.pop().op);
          }
          s.push(new Operator(exp, 1));
          break;
      case "*":
      case "/":
          while (!s.isEmpty() && s.peek().level >= 2) {
            result.add(s.pop().op);
          }
          s.push(new Operator(exp, 2));
          break;
      case "(":
          s.push(new Operator(exp, 0));
          break;
      case ")":
          while (!s.isEmpty()) {
            if ("(".equals(s.peek().op)) {
              s.pop();
              break;
            } else {
              result.add(s.pop().op);
            }
          }
          break;
      default:
          throw new UnsupportedOperationException(exp);
      }
    }
    while (!s.isEmpty()) {
      result.add(s.pop().op);
    }
    return result;
  }

  // 分解代数式
  public static List<String> split(String exp) {
    String regx = "\\s+|\\" + String.join("|\\", OPS);
    List<String> result = new ArrayList<>();
    Pattern p = Pattern.compile(regx);
    Matcher m = p.matcher(exp);
    int start = 0;
    while (m.find()) {
      String s = exp.substring(start, m.start()).trim();
      String g = m.group().trim();
      if (s.length() > 0) result.add(s);
      if (g.length() > 0) result.add(g);
      start = m.end();
    }
    String l = exp.substring(start).trim();
    if (l.length() > 0) result.add(l);
    return result;
  }

  // “算符”类
  static class Operator {
    String op;// 符号
    int level;// 优先级
    public Operator(String op, int level) {
      this.op = op;
      this.level = level;
    }
  }

}

  求值时先把(式二)转换成对象树,再遍历树中元素,代入具体数值后求出结果。这种计算方法也被称为解释器模式。
  看下生成对象树的代码。

// 表达式
public abstract class Expression {

  // 求值
  public abstract Double interpret(Map<String, Double> vars);

}

// 算术表达式
public abstract class ArithExpression extends Expression {

  protected Expression a;
  protected Expression b;

  protected ArithExpression(Expression a, Expression b) {
    this.a = a;
    this.b = b;
  }

}

// 加
public class AddExpression extends ArithExpression {

  public AddExpression(Expression a, Expression b) {
    super(a, b);
  }

  @Override
  public Double interpret(java.util.Map<String, Double> vars) {
    return b.interpret(vars) + a.interpret(vars);
  }

}

// 减
public class SubExpression extends ArithExpression {

  public SubExpression(Expression a, Expression b) {
    super(a, b);
  }

  @Override
  public Double interpret(java.util.Map<String, Double> vars) {
    return b.interpret(vars) - a.interpret(vars);
  }

}

// 乘
public class MulExpression extends ArithExpression {

  public MulExpression(Expression a, Expression b) {
    super(a, b);
  }

  @Override
  public Double interpret(java.util.Map<String, Double> vars) {
    return b.interpret(vars) * a.interpret(vars);
  }

}

// 除
public class DivExpression extends ArithExpression {

  public DivExpression(Expression a, Expression b) {
    super(a, b);
  }

  @Override
  public Double interpret(java.util.Map<String, Double> vars) {
    return b.interpret(vars) / a.interpret(vars);
  }

}

// 数值表达式
public class VarExpression extends Expression {

  private String key;

  public VarExpression(String key) {
    this.key = key;
  }

  @Override
  public Double interpret(java.util.Map<String, Double> vars) {
    return vars.get(this.key);
  }

}

// 对象树工具
public class ExpressionTreeUtil {

  // 生成对象树
  public static Expression create(java.util.List<String> rpn) {
    java.util.Stack<Expression> s = new java.util.Stack<>();
    for (String exp : rpn) {
      switch (exp) {
      case "+":
          s.push(new AddExpression(s.pop(), s.pop()));
          break;
      case "-":
          s.push(new SubExpression(s.pop(), s.pop()));
          break;
      case "*":
          s.push(new MulExpression(s.pop(), s.pop()));
          break;
      case "/":
          s.push(new DivExpression(s.pop(), s.pop()));
          break;
      default:
          s.push(new VarExpression(exp));
      }
    }
    return s.pop();
  }

}

  此例生成的对象树如下图所示。
  
  从图中可以看出,末端元素负责提供数值,复合元素负责计算。计算任务将从根元素开始,至遍历完整棵对象树结束。
  使用效果如下。

import java.util.*;

// 测试类
public class Test {

  public void test() {
    // 输入四则运算代数式
    String exp = "a * (b + c) + d";
    // 将四则运算代数式转换成逆波兰表达式
    List<String> rpn = ExpressionUtil.convertToRPN(exp);
    // 生成对象树
    Expression tree = ExpressionTreeUtil.create(rpn);
    // 代入具体数值
    Map<String, Double> vars = new HashMap<>();
    vars.put("a", 2.0);
    vars.put("b", 4.0);
    vars.put("c", 6.0);
    vars.put("d", 8.0);
    // 计算结果
    Double result = tree.interpret(vars);
  }

}
posted on 2025-03-28 21:08  星辰河岳  阅读(25)  评论(0)    收藏  举报