查看: 5828| 回复: 13
跳转到指定楼层
上一主题 下一主题
收起左侧

Calculator类题目的高级通用解法

   
全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
本帖最后由 magicsets 于 2018-1-12 04:14 编辑

这个方法是之前写在另一个帖子里的,觉得有点通用价值所以单独发一下

考虑Calculator的各种题目,例如:
http://www.1point3acres.com/bbs/thread-218681-1-1.html
http://www.1point3acres.com/bbs/thread-266657-1-1.html
http://www.1point3acres.com/bbs/thread-297291-1-1.html
http://www.1point3acres.com/bbs/thread-304410-1-1.html

其实主要区别就是使用的运算符(operator)不同,而一个运算符可以由以下性质所描述:
(1) 词法,也就符号本身
例如 '+','-','*','\'

(2) 元数(arity)
大部分操作符都是二元的,例如加减乘除,也有题目中需要支持一元的取负('-')运算符

(3) 优先级(precedence)
一般用一个整数表示

(4) 结合性(associativity)
例如加减乘除都是左结合的,也就是同优先级下,按从左往右的顺序计算;负号则是右结合;也有左右都不结合的运算

结合性有一些表示法,参考这里:http://www.swi-prolog.org/pldoc/man?predicate=op/3

(5) 语义(semantics)
运算符的语义可以表示为宿主语言下的一个函数,例如在Java下,加法的语义就是(a, b) -> a + b


在此基础上,我们可以只写一份基础设施代码,然后对于任意一道变形题目,只需要额外的几行代码描述其中运算符的以上几点性质,就可以自动生成对应功能的Calculator

参考代码如下:
  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Stack;

  6. public class Main {
  7.   public static void main(String[] args) {
  8.     // 一个使用样例
  9.     Calculator calc = new Calculator();

  10.     // 中间的数字是优先级(precedence),数字越小优先级越高
  11.     // 这里限定二元运算符是left associative,一元运算符是right associative
  12.     calc.registerOperation("+", 500, (a, b) -> a + b);
  13.     calc.registerOperation("-", 500, (a, b) -> a - b);
  14.     calc.registerOperation("*", 400, (a, b) -> a * b);
  15.     calc.registerOperation("/", 400, (a, b) -> a / b);
  16.     calc.registerOperation("^", 200, (a, b) -> (int) Math.pow(a, b));
  17.     calc.registerOperation("-", 100, (a) -> -a);

  18.     // 某道Calculator变形题,重定义了"&"和"|"的语义
  19.     calc.registerOperation("&", 600, (a, b) -> Math.max(a, b));
  20.     calc.registerOperation("|", 800, (a, b) -> Math.min(a, b));

  21.     // 也可以支持函数,函数本质上是前缀表达式,就不需要优先级信息了
  22.     calc.registerFunction("abs", (int ...a) -> Math.abs(a[0]));
  23.     calc.registerFunction("min", (int ...a) -> Arrays.stream(a).min().getAsInt());
  24.     calc.registerFunction("max", (int ...a) -> Arrays.stream(a).max().getAsInt());


  25.     System.out.println(calc.evaluate("1 + 2 * 3 ^ abs(6 - 3 * 3) + (-8) / (-2)"));
  26.     System.out.println(calc.evaluate("min(-3 * 2, -4, -5) * max(5, 6, 7, min(8, 9))"));
  27.   }
  28. }

  29. /******************************************************************************
  30. 样例:使用下面的Solution类可以通过LeetCode 224

  31. class Solution {
  32.   private static Calculator calc = new Calculator();
  33.   static {
  34.     calc.registerOperation("+", 500, (a, b) -> a + b);
  35.     calc.registerOperation("-", 500, (a, b) -> a - b);
  36.     calc.registerOperation("-", 100, (a) -> -a);
  37.   }

  38.   public int calculate(String s) {
  39.     return calc.evaluate(s);
  40.   }
  41. }

  42. ******************************************************************************/

  43. /******************************************************************************
  44. 样例:使用下面的Solution类可以通过LeetCode 227

  45. class Solution {
  46.   private static Calculator calc = new Calculator();
  47.   static {
  48.     calc.registerOperation("+", 500, (a, b) -> a + b);
  49.     calc.registerOperation("-", 500, (a, b) -> a - b);
  50.     calc.registerOperation("*", 400, (a, b) -> a * b);
  51.     calc.registerOperation("/", 400, (a, b) -> a / b);
  52.     calc.registerOperation("-", 100, (a) -> -a);
  53.   }

  54.   public int calculate(String s) {
  55.     return calc.evaluate(s);
  56.   }
  57. }

  58. ******************************************************************************/

  59. class Calculator {
  60.   private Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  61.   private Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  62.   private Map<String, Function> functions;

  63.   public Calculator() {
  64.     this.unaryOperations =
  65.         new HashMap<String, OperationInfo<UnaryOperation>>();
  66.     this.binaryOperations =
  67.         new HashMap<String, OperationInfo<BinaryOperation>>();
  68.     this.functions = new HashMap<String, Function>();
  69.   }

  70.   // 注册一个一元运算符
  71.   public void registerOperation(String operator, int precedence,
  72.                                 UnaryOperation operation) {
  73.     unaryOperations.put(
  74.         operator, new OperationInfo<UnaryOperation>(precedence, operation));
  75.   }

  76.   // 注册一个二元运算符
  77.   public void registerOperation(String operator, int precedence,
  78.                                 BinaryOperation operation) {
  79.     binaryOperations.put(
  80.         operator, new OperationInfo<BinaryOperation>(precedence, operation));
  81.   }

  82.   // 注册一个函数
  83.   public void registerFunction(String name, Function function) {
  84.     functions.put(name, function);
  85.   }

  86.   // 调用Executor对表达式进行计算
  87.   public int evaluate(String expression) {
  88.     CalculatorExecutor executor =
  89.         new CalculatorExecutor(unaryOperations, binaryOperations,
  90.                                functions, expression);
  91.     return executor.run();
  92.   }
  93. }


  94. // 对一元运算符的抽象
  95. interface UnaryOperation {
  96.   public int apply(int operand);
  97. }

  98. // 对二元运算符的抽象
  99. interface BinaryOperation  {
  100.   public int apply(int lhs, int rhs);
  101. }

  102. // 对函数的抽象
  103. interface Function {
  104.   public int apply(int ...operands);
  105. }


  106. // 用于打包operation和对应的precedence信息的小类
  107. class OperationInfo<T> {
  108.   public final int precedence;
  109.   public final T operation;
  110.   public OperationInfo(int precedence, T operation) {
  111.     this.precedence = precedence;
  112.     this.operation = operation;
  113.   }
  114. }

  115. // LL(1) Recursive Descent Syntax Directed Translator
  116. class CalculatorExecutor {
  117.   private final Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  118.   private final Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  119.   private final Map<String, Function> functions;

  120.   private final String stream;
  121.   private int position;

  122.   // 词法部分也合并到这个类里了
  123.   private enum TokenType {
  124.     INTEGER,
  125.     NAME,
  126.     OPERATOR,
  127.     LPAREN,
  128.     RPAREN,
  129.     COMMA,
  130.     EOS,
  131.     ERROR
  132.   }

  133.   private class Token {
  134.     public final TokenType type;
  135.     public final Object payload;
  136.     public final int columnNumber;
  137.     public Token(TokenType type, int columnNumber) {
  138.       this(type, columnNumber, null);
  139.     }
  140.     public Token(TokenType type, int columnNumber, Object payload) {
  141.       this.type = type;
  142.       this.payload = payload;
  143.       this.columnNumber = columnNumber;
  144.     }
  145.   }

  146.   private Stack<Token> tokens;

  147.   public CalculatorExecutor(
  148.       Map<String, OperationInfo<UnaryOperation>> unaryOperations,
  149.       Map<String, OperationInfo<BinaryOperation>> binaryOperations,
  150.       Map<String, Function> functions,
  151.       String stream) {
  152.     this.unaryOperations = unaryOperations;
  153.     this.binaryOperations = binaryOperations;
  154.     this.functions = functions;
  155.     this.stream = stream;
  156.     this.position = 0;
  157.     this.tokens = new Stack<Token>();
  158.   }

  159.   public int run() {
  160.     int value = evalExpr(Integer.MAX_VALUE);
  161.     passNextToken(TokenType.EOS);
  162.     return value;
  163.   }

  164.   //////////////////////////////////////////////////////////////////////////////
  165.   // 语法处理部分

  166.   private int evalExpr(int precedence) {
  167.     int value = evalFactor(precedence);

  168.     // 二元运算符,注意这里对优先级的处理方法
  169.     while (hasNextToken(TokenType.OPERATOR)) {
  170.       Token token = nextToken();
  171.       OperationInfo<BinaryOperation> info =
  172.           binaryOperations.get((String)token.payload);
  173.       if (info == null || info.precedence >= precedence) {
  174.         pushBack(token);
  175.         break;
  176.       }
  177.       value = info.operation.apply(value, evalExpr(info.precedence));
  178.     }
  179.     return value;
  180.   }

  181.   private int evalFactor(int precedence) {
  182.     Token token = nextToken();

  183.     // Case 1. 数字自身
  184.     if (token.type == TokenType.INTEGER) {
  185.       return (Integer)token.payload;
  186.     }

  187.     // Case 2. 一元运算符
  188.     if (token.type == TokenType.OPERATOR) {
  189.       OperationInfo<UnaryOperation> info =
  190.           unaryOperations.get((String)token.payload);
  191.       if (info == null || info.precedence > precedence) {
  192.         error(token);
  193.       }
  194.       return info.operation.apply(evalExpr(info.precedence));
  195.     }

  196.     // Case 3. 函数调用
  197.     if (token.type == TokenType.NAME) {
  198.       Function func = functions.get((String)token.payload);
  199.       if (func == null) {
  200.         error(token);
  201.       }
  202.       ArrayList<Integer> args = new ArrayList<Integer>();
  203.       passNextToken(TokenType.LPAREN);
  204.       while (!hasNextToken(TokenType.RPAREN)) {
  205.         args.add(evalExpr(Integer.MAX_VALUE));
  206.         if (!hasNextToken(TokenType.COMMA)) {
  207.           break;
  208.         }
  209.         nextToken();
  210.       }
  211.       passNextToken(TokenType.RPAREN);
  212.       return func.apply(args.stream().mapToInt(x -> x).toArray());
  213.     }

  214.     // Case 4. 括号表达式
  215.     if (token.type == TokenType.LPAREN) {
  216.       int value = evalExpr(Integer.MAX_VALUE);
  217.       Token rp = nextToken();
  218.       if (rp.type != TokenType.RPAREN) {
  219.         error(token);
  220.       }
  221.       return value;
  222.     }

  223.     error(token);
  224.     return 0;
  225.   }

  226.   //////////////////////////////////////////////////////////////////////////////
  227.   // 词法处理部分

  228.   private boolean hasNextToken(TokenType ...expected) {
  229.     Token token = nextToken();
  230.     boolean status = Arrays.stream(expected).anyMatch(x -> x == token.type);
  231.     pushBack(token);
  232.     return status;
  233.   }

  234.   private void passNextToken(TokenType ...expected) {
  235.     Token token = nextToken();
  236.     if (!Arrays.stream(expected).anyMatch(x -> x == token.type)) {
  237.       error(token);
  238.     }
  239.   }

  240.   private void pushBack(Token token) {
  241.     tokens.add(token);
  242.   }

  243.   private Token nextToken() {
  244.     if (!tokens.isEmpty()) {
  245.       return tokens.pop();
  246.     }
  247.     while (position < stream.length() &&
  248.            Character.isWhitespace(stream.charAt(position))) {
  249.       ++position;
  250.     }

  251.     if (position >= stream.length()) {
  252.       return new Token(TokenType.EOS, position);
  253.     }

  254.     final int start = position;
  255.     final char ch = stream.charAt(position++);

  256.     switch (ch) {
  257.       case '(': return new Token(TokenType.LPAREN, start);
  258.       case ')': return new Token(TokenType.RPAREN, start);
  259.       case ',': return new Token(TokenType.COMMA, start);
  260.       default:  --position; break;
  261.     }

  262.     if (Character.isDigit(ch)) {
  263.       return nextNumber();
  264.     }
  265.     if (Character.isAlphabetic(ch)) {
  266.       return nextName();
  267.     }
  268.     return nextOperator();
  269.   }

  270.   private Token nextNumber() {
  271.     final int start = position;
  272.     while (position < stream.length() &&
  273.            Character.isDigit(stream.charAt(position))) {
  274.       ++position;
  275.     }
  276.     return new Token(TokenType.INTEGER, start,
  277.                      Integer.parseInt(stream.substring(start, position)));
  278.   }

  279.   private Token nextName() {
  280.     final int start = position;
  281.     while (position < stream.length() &&
  282.            Character.isAlphabetic(stream.charAt(position))) {
  283.       ++position;
  284.     }
  285.     return new Token(TokenType.NAME, start,
  286.                      stream.substring(start, position));
  287.   }

  288.   private Token nextOperator() {
  289.     final int start = position;
  290.     while (position < stream.length()) {
  291.       final char ch = stream.charAt(position);
  292.       if (Character.isWhitespace(ch) ||
  293.           Character.isDigit(ch) ||
  294.           Character.isAlphabetic(ch) ||
  295.           ch == '(' || ch == ')' || ch == ',') {
  296.         break;
  297.       }
  298.       ++position;
  299.     }
  300.     return new Token(TokenType.OPERATOR, start,
  301.                      stream.substring(start, position));
  302.   }

  303.   private void error(Token token) {
  304.     throw new RuntimeException(
  305.         "Syntax error: unexpected token at column " +
  306.         (token.columnNumber+1) + " -- " + token.type.name() +
  307.         (token.payload == null ? "" : " (" + token.payload.toString() + ")"));
  308.   }
  309. }
复制代码

评分

参与人数 13大米 +42 收起 理由
jesse1204 + 2 很有用的信息!
robinsnowy + 1 很有用的信息!
ximilara + 2 给你点个赞!
candyz + 2 很有用的信息!
guagua_MEMS + 2 很有用的信息!

查看全部评分


上一篇:JAVA面试题库/书
下一篇:请教一个在地里看到的面经题(PE)
全局:

你总结的这么多这么好,应该给你大米的。我再给你点!
回复

使用道具 举报

推荐
 楼主| magicsets 2018-1-13 02:24:11 | 只看该作者
全局:
本帖最后由 magicsets 于 2018-1-13 02:31 编辑
biomedicineman 发表于 2018-1-12 15:00
是啊是啊。最后那种情况压根就不是计算器了。。。所以我最后这个面试的时候没做出来。。。至今也没时间fi ...

我把这个也写了下,但是代码量多了不少... 主要是110行到294行的语法树的处理方面,供参考~ (PS 感谢你的大米!不用再加了哈)

(不过目前这个代码不考虑运算符的交换性、结合性和分配性 -- 比如(1 + x - 2) * 3可以化简为x*3 - 3 -- 要支持这些的话要写一个rule-based system对语法树进行各种变换)
  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Stack;
  6. import java.util.StringJoiner;

  7. public class Main {
  8.   public static void main(String[] args) {
  9.     // 一个使用样例
  10.     Calculator calc = new Calculator();

  11.     // 中间的数字是优先级(precedence),数字越小优先级越高
  12.     // 这里限定二元运算符是left associative,一元运算符是right associative
  13.     calc.registerOperation("+", 500, (a, b) -> a + b);
  14.     calc.registerOperation("-", 500, (a, b) -> a - b);
  15.     calc.registerOperation("*", 400, (a, b) -> a * b);
  16.     calc.registerOperation("/", 400, (a, b) -> a / b);
  17.     calc.registerOperation("^", 200, (a, b) -> (int) Math.pow(a, b));
  18.     calc.registerOperation("-", 100, (a) -> -a);

  19.     // 也可以支持函数,函数本质上是前缀表达式,就不需要优先级信息了
  20.     calc.registerFunction("abs", (int ...a) -> Math.abs(a[0]));
  21.     calc.registerFunction("min", (int ...a) -> Arrays.stream(a).min().getAsInt());
  22.     calc.registerFunction("max", (int ...a) -> Arrays.stream(a).max().getAsInt());


  23.     calc.setVariable("software", 3);
  24.     String s1 = calc.evaluate("software * 2 + (1 - hardware) * (2 + 3) + min(a + 5, 3 * 8)");
  25.     System.out.println("s1 = " + s1);
  26.     // 输出:s1 = 6 + (1 - hardware) * 5 + min(a + 5, 24)

  27.     calc.setVariable("hardware",  5);
  28.     String s2 = calc.evaluate(s1);
  29.     System.out.println("s2 = " + s2);
  30.     // 输出:s2 = -14 + min(a + 5, 24)

  31.     calc.setVariable("a", 10);
  32.     String s3 = calc.evaluate(s2);
  33.     System.out.println("s3 = " + s3);
  34.     // 输出:s3 = 1
  35.   }
  36. }

  37. class Calculator {
  38.   private Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  39.   private Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  40.   private Map<String, Function> functions;
  41.   private Map<String, Integer> environment;

  42.   public Calculator() {
  43.     this.unaryOperations =
  44.         new HashMap<String, OperationInfo<UnaryOperation>>();
  45.     this.binaryOperations =
  46.         new HashMap<String, OperationInfo<BinaryOperation>>();
  47.     this.functions = new HashMap<String, Function>();
  48.     this.environment = new HashMap<String, Integer>();
  49.   }

  50.   // 注册一个一元运算符
  51.   public void registerOperation(String operator, int precedence,
  52.                                 UnaryOperation operation) {
  53.     unaryOperations.put(
  54.         operator, new OperationInfo<UnaryOperation>(precedence, operation));
  55.   }

  56.   // 注册一个二元运算符
  57.   public void registerOperation(String operator, int precedence,
  58.                                 BinaryOperation operation) {
  59.     binaryOperations.put(
  60.         operator, new OperationInfo<BinaryOperation>(precedence, operation));
  61.   }

  62.   // 注册一个函数
  63.   public void registerFunction(String name, Function function) {
  64.     functions.put(name, function);
  65.   }

  66.   // 设置变量的值
  67.   public void setVariable(String name, int value) {
  68.     environment.put(name, value);
  69.   }

  70.   // 调用Executor对表达式进行计算
  71.   public String evaluate(String expression) {
  72.     CalculatorExecutor executor =
  73.         new CalculatorExecutor(unaryOperations, binaryOperations,
  74.                                functions, environment, expression);
  75.     return executor.run().toString();
  76.   }
  77. }


  78. // 对一元运算符的抽象
  79. interface UnaryOperation {
  80.   public int apply(int operand);
  81. }

  82. // 对二元运算符的抽象
  83. interface BinaryOperation  {
  84.   public int apply(int lhs, int rhs);
  85. }

  86. // 对函数的抽象
  87. interface Function {
  88.   public int apply(int ...operands);
  89. }


  90. // 语法树
  91. abstract class Expression {
  92.   public abstract int getPrecedence();
  93.   public abstract String toString();
  94.   public abstract Expression eval(Map<String, Integer> environment);

  95.   protected String addParenthese(String s, int innerPrecedence) {
  96.     return innerPrecedence >= getPrecedence() ? "(" + s + ")" : s;
  97.   }
  98. }

  99. // 语法树:一元表达式
  100. class UnaryExpression extends Expression {
  101.   private final String operator;
  102.   private final UnaryOperation operation;
  103.   private final int precedence;
  104.   private final Expression operand;

  105.   public UnaryExpression(String operator, UnaryOperation operation,
  106.                          int precedence, Expression operand) {
  107.     this.operator = operator;
  108.     this.operation = operation;
  109.     this.precedence = precedence;
  110.     this.operand = operand;
  111.   }

  112.   @Override
  113.   public Expression eval(Map<String, Integer> environment) {
  114.     Expression result = operand.eval(environment);
  115.     if (result instanceof Literal) {
  116.       return new Literal(operation.apply(((Literal)result).value));
  117.     }
  118.     return new UnaryExpression(operator, operation, precedence, result);
  119.   }

  120.   @Override
  121.   public int getPrecedence() {
  122.     return precedence;
  123.   }

  124.   @Override
  125.   public String toString() {
  126.     return operator + addParenthese(operand.toString(), operand.getPrecedence());
  127.   }
  128. }

  129. // 语法树:二元表达式
  130. class BinaryExpression extends Expression {
  131.   private final String operator;
  132.   private final BinaryOperation operation;
  133.   private final int precedence;
  134.   private final Expression lhsOperand;
  135.   private final Expression rhsOperand;

  136.   public BinaryExpression(String operator,
  137.                           BinaryOperation operation, int precedence,
  138.                           Expression lhsOperand, Expression rhsOperand) {
  139.     this.operator = operator;
  140.     this.operation = operation;
  141.     this.precedence = precedence;
  142.     this.lhsOperand = lhsOperand;
  143.     this.rhsOperand = rhsOperand;
  144.   }

  145.   @Override
  146.   public Expression eval(Map<String, Integer> environment) {
  147.     Expression lhsResult = lhsOperand.eval(environment);
  148.     Expression rhsResult = rhsOperand.eval(environment);
  149.     if (lhsResult instanceof Literal && rhsResult instanceof Literal) {
  150.       return new Literal(
  151.           operation.apply(((Literal)lhsResult).value, ((Literal)rhsResult).value));
  152.     }
  153.     return new BinaryExpression(operator, operation, precedence, lhsResult, rhsResult);
  154.   }

  155.   @Override
  156.   public int getPrecedence() {
  157.     return precedence;
  158.   }

  159.   @Override
  160.   public String toString() {
  161.     return addParenthese(lhsOperand.toString(), lhsOperand.getPrecedence() - 1) +
  162.            " " + operator + " " +
  163.            addParenthese(rhsOperand.toString(), rhsOperand.getPrecedence());
  164.   }
  165. }

  166. // 语法树:函数调用
  167. class FunctionCall extends Expression {
  168.   private final String functor;
  169.   private final Function function;
  170.   private final Expression[] arguments;

  171.   public FunctionCall(String functor,
  172.                       Function function,
  173.                       Expression[] arguments) {
  174.     this.functor = functor;
  175.     this.function = function;
  176.     this.arguments = arguments;
  177.   }

  178.   @Override
  179.   public Expression eval(Map<String, Integer> environment) {
  180.     Expression[] results = new Expression[arguments.length];
  181.     for (int i = 0; i < arguments.length; ++i) {
  182.       results[i] = arguments[i].eval(environment);
  183.     }

  184.     boolean allLiterals =
  185.         Arrays.stream(results).allMatch(arg -> arg instanceof Literal);

  186.     if (allLiterals) {
  187.       int[] literals = new int[arguments.length];
  188.       for (int i = 0; i < arguments.length; ++i) {
  189.         literals[i] = ((Literal)results[i]).value;
  190.       }
  191.       return new Literal(function.apply(literals));
  192.     }
  193.     return new FunctionCall(functor, function, results);
  194.   }

  195.   @Override
  196.   public int getPrecedence() {
  197.     return 0;
  198.   }

  199.   @Override
  200.   public String toString() {
  201.     StringJoiner argJoiner = new StringJoiner(", ");
  202.     for (Expression arg : arguments) {
  203.       argJoiner.add(arg.toString());
  204.     }
  205.     return functor + "(" + argJoiner.toString() + ")";
  206.   }
  207. }

  208. // 语法树:常量值
  209. class Literal extends Expression {
  210.   public final int value;

  211.   public Literal(int value) {
  212.     this.value = value;
  213.   }

  214.   @Override
  215.   public Expression eval(Map<String, Integer> environment) {
  216.     return this;
  217.   }

  218.   @Override
  219.   public int getPrecedence() {
  220.     return 0;
  221.   }

  222.   @Override
  223.   public String toString() {
  224.     return String.valueOf(value);
  225.   }
  226. }

  227. // 语法树:变量
  228. class Variable extends Expression {
  229.   private final String name;

  230.   public Variable(String name) {
  231.     this.name = name;
  232.   }

  233.   @Override
  234.   public Expression eval(Map<String, Integer> environment) {
  235.     Integer value = environment.get(name);
  236.     return value == null ? this : new Literal(value);
  237.   }

  238.   @Override
  239.   public int getPrecedence() {
  240.     return 0;
  241.   }

  242.   @Override
  243.   public String toString() {
  244.     return name;
  245.   }
  246. }

  247. // 用于打包operation和对应的precedence信息的小类
  248. class OperationInfo<T> {
  249.   public final int precedence;
  250.   public final T operation;
  251.   public OperationInfo(int precedence, T operation) {
  252.     this.precedence = precedence;
  253.     this.operation = operation;
  254.   }
  255. }

  256. // LL(1) Recursive Descent Syntax Directed Translator
  257. class CalculatorExecutor {
  258.   private final Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  259.   private final Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  260.   private final Map<String, Function> functions;
  261.   private Map<String, Integer> environment;

  262.   private final String stream;
  263.   private int position;

  264.   // 词法部分也合并到这个类里了
  265.   private enum TokenType {
  266.     INTEGER,
  267.     NAME,
  268.     OPERATOR,
  269.     LPAREN,
  270.     RPAREN,
  271.     COMMA,
  272.     EOS,
  273.     ERROR
  274.   }

  275.   private class Token {
  276.     public final TokenType type;
  277.     public final Object payload;
  278.     public final int columnNumber;
  279.     public Token(TokenType type, int columnNumber) {
  280.       this(type, columnNumber, null);
  281.     }
  282.     public Token(TokenType type, int columnNumber, Object payload) {
  283.       this.type = type;
  284.       this.payload = payload;
  285.       this.columnNumber = columnNumber;
  286.     }
  287.   }

  288.   private Stack<Token> tokens;

  289.   public CalculatorExecutor(
  290.       Map<String, OperationInfo<UnaryOperation>> unaryOperations,
  291.       Map<String, OperationInfo<BinaryOperation>> binaryOperations,
  292.       Map<String, Function> functions,
  293.       Map<String, Integer> environment,
  294.       String stream) {
  295.     this.unaryOperations = unaryOperations;
  296.     this.binaryOperations = binaryOperations;
  297.     this.functions = functions;
  298.     this.environment = environment;
  299.     this.stream = stream;
  300.     this.position = 0;
  301.     this.tokens = new Stack<Token>();
  302.   }

  303.   public Expression run() {
  304.     Expression expr = parseExpr(Integer.MAX_VALUE);
  305.     passNextToken(TokenType.EOS);
  306.     return expr.eval(environment);
  307.   }

  308.   //////////////////////////////////////////////////////////////////////////////
  309.   // 语法处理部分

  310.   private Expression parseExpr(int precedence) {
  311.     Expression expr = parseFactor(precedence);

  312.     // 二元运算符,注意这里对优先级的处理方法
  313.     while (hasNextToken(TokenType.OPERATOR)) {
  314.       Token token = nextToken();
  315.       String operator = (String)token.payload;
  316.       OperationInfo<BinaryOperation> info = binaryOperations.get(operator);
  317.       if (info == null || info.precedence >= precedence) {
  318.         pushBack(token);
  319.         break;
  320.       }
  321.       expr = new BinaryExpression(operator, info.operation, info.precedence,
  322.                                   expr, parseExpr(info.precedence));
  323.     }
  324.     return expr;
  325.   }

  326.   private Expression parseFactor(int precedence) {
  327.     Token token = nextToken();

  328.     // Case 1. 数字自身
  329.     if (token.type == TokenType.INTEGER) {
  330.       return new Literal((Integer)token.payload);
  331.     }

  332.     // Case 2. 一元运算符
  333.     if (token.type == TokenType.OPERATOR) {
  334.       String operator = (String)token.payload;
  335.       OperationInfo<UnaryOperation> info = unaryOperations.get(operator);
  336.       if (info == null || info.precedence > precedence) {
  337.         error(token);
  338.       }
  339.       return new UnaryExpression(operator, info.operation,
  340.                                  info.precedence, parseExpr(info.precedence));
  341.     }

  342.     // Case 3. 函数调用或者变量
  343.     if (token.type == TokenType.NAME) {
  344.       if (hasNextToken(TokenType.LPAREN)) {
  345.         String functor = (String)token.payload;
  346.         Function function = functions.get(functor);
  347.         if (function == null) {
  348.           error(token);
  349.         }
  350.         ArrayList<Expression> args = new ArrayList<Expression>();
  351.         passNextToken(TokenType.LPAREN);
  352.         while (!hasNextToken(TokenType.RPAREN)) {
  353.           args.add(parseExpr(Integer.MAX_VALUE));
  354.           if (!hasNextToken(TokenType.COMMA)) {
  355.             break;
  356.           }
  357.           nextToken();
  358.         }
  359.         passNextToken(TokenType.RPAREN);
  360.         return new FunctionCall(
  361.             functor, function, args.toArray(new Expression[args.size()]));
  362.       } else {
  363.         return new Variable((String)token.payload);
  364.       }
  365.     }

  366.     // Case 4. 括号表达式
  367.     if (token.type == TokenType.LPAREN) {
  368.       Expression expr = parseExpr(Integer.MAX_VALUE);
  369.       Token rp = nextToken();
  370.       if (rp.type != TokenType.RPAREN) {
  371.         error(token);
  372.       }
  373.       return expr;
  374.     }

  375.     error(token);
  376.     return null;
  377.   }

  378.   //////////////////////////////////////////////////////////////////////////////
  379.   // 词法处理部分

  380.   private boolean hasNextToken(TokenType ...expected) {
  381.     Token token = nextToken();
  382.     boolean status = Arrays.stream(expected).anyMatch(x -> x == token.type);
  383.     pushBack(token);
  384.     return status;
  385.   }

  386.   private void passNextToken(TokenType ...expected) {
  387.     Token token = nextToken();
  388.     if (!Arrays.stream(expected).anyMatch(x -> x == token.type)) {
  389.       error(token);
  390.     }
  391.   }

  392.   private void pushBack(Token token) {
  393.     tokens.add(token);
  394.   }

  395.   private Token nextToken() {
  396.     if (!tokens.isEmpty()) {
  397.       return tokens.pop();
  398.     }
  399.     while (position < stream.length() &&
  400.            Character.isWhitespace(stream.charAt(position))) {
  401.       ++position;
  402.     }

  403.     if (position >= stream.length()) {
  404.       return new Token(TokenType.EOS, position);
  405.     }

  406.     final int start = position;
  407.     final char ch = stream.charAt(position++);

  408.     switch (ch) {
  409.       case '(': return new Token(TokenType.LPAREN, start);
  410.       case ')': return new Token(TokenType.RPAREN, start);
  411.       case ',': return new Token(TokenType.COMMA, start);
  412.       default:  --position; break;
  413.     }

  414.     if (Character.isDigit(ch)) {
  415.       return nextNumber();
  416.     }
  417.     if (Character.isAlphabetic(ch)) {
  418.       return nextName();
  419.     }
  420.     return nextOperator();
  421.   }

  422.   private Token nextNumber() {
  423.     final int start = position;
  424.     while (position < stream.length() &&
  425.            Character.isDigit(stream.charAt(position))) {
  426.       ++position;
  427.     }
  428.     return new Token(TokenType.INTEGER, start,
  429.                      Integer.parseInt(stream.substring(start, position)));
  430.   }

  431.   private Token nextName() {
  432.     final int start = position;
  433.     while (position < stream.length() &&
  434.            Character.isAlphabetic(stream.charAt(position))) {
  435.       ++position;
  436.     }
  437.     return new Token(TokenType.NAME, start,
  438.                      stream.substring(start, position));
  439.   }

  440.   private Token nextOperator() {
  441.     final int start = position;
  442.     while (position < stream.length()) {
  443.       final char ch = stream.charAt(position);
  444.       if (Character.isWhitespace(ch) ||
  445.           Character.isDigit(ch) ||
  446.           Character.isAlphabetic(ch) ||
  447.           ch == '(' || ch == ')' || ch == ',') {
  448.         break;
  449.       }
  450.       ++position;
  451.     }
  452.     return new Token(TokenType.OPERATOR, start,
  453.                      stream.substring(start, position));
  454.   }

  455.   private void error(Token token) {
  456.     throw new RuntimeException(
  457.         "Syntax error: unexpected token at column " +
  458.         (token.columnNumber+1) + " -- " + token.type.name() +
  459.         (token.payload == null ? "" : " (" + token.payload.toString() + ")"));
  460.   }
  461. }
复制代码

评分

参与人数 1大米 +3 收起 理由
helloteacha + 3 很有用的信息!

查看全部评分

回复

使用道具 举报

推荐
 楼主| magicsets 2018-1-12 13:29:04 | 只看该作者
全局:
本帖最后由 magicsets 于 2018-1-12 13:30 编辑
biomedicineman 发表于 2018-1-12 12:13
intuit面试calculator题followup 3, 要求带入variable

比如 given:

这个需要维护一个运行时环境(Runtime Environment),大概添加20行代码可以实现,参考下面代码的第34、35行。

此外,涉及到运行时环境的话,再加一点点代码就可以支持变量赋值了,然后再加一点点代码支持Multiple Statement,然后支持分支和循环,你就得到了一个小小的解释型程序语言... 再支持一下数组,这个语言就图灵完备了
  1. import java.util.ArrayList;
  2. import java.util.Arrays;
  3. import java.util.HashMap;
  4. import java.util.Map;
  5. import java.util.Stack;

  6. public class Main {
  7.   public static void main(String[] args) {
  8.     // 一个使用样例
  9.     Calculator calc = new Calculator();

  10.     // 中间的数字是优先级(precedence),数字越小优先级越高
  11.     // 这里限定二元运算符是left associative,一元运算符是right associative
  12.     calc.registerOperation("+", 500, (a, b) -> a + b);
  13.     calc.registerOperation("-", 500, (a, b) -> a - b);
  14.     calc.registerOperation("*", 400, (a, b) -> a * b);
  15.     calc.registerOperation("/", 400, (a, b) -> a / b);
  16.     calc.registerOperation("^", 200, (a, b) -> (int) Math.pow(a, b));
  17.     calc.registerOperation("-", 100, (a) -> -a);

  18.     // 某道Calculator变形题,重定义了"&"和"|"的语义
  19.     calc.registerOperation("&", 600, (a, b) -> Math.max(a, b));
  20.     calc.registerOperation("|", 800, (a, b) -> Math.min(a, b));

  21.     // 也可以支持函数,函数本质上是前缀表达式,就不需要优先级信息了
  22.     calc.registerFunction("abs", (int ...a) -> Math.abs(a[0]));
  23.     calc.registerFunction("min", (int ...a) -> Arrays.stream(a).min().getAsInt());
  24.     calc.registerFunction("max", (int ...a) -> Arrays.stream(a).max().getAsInt());


  25.     System.out.println(calc.evaluate("1 + 2 * 3 ^ abs(6 - 3 * 3) + (-8) / (-2)"));
  26.     System.out.println(calc.evaluate("min(-3 * 2, -4, -5) * max(5, 6, 7, min(8, 9))"));

  27.     calc.setVariable("software", 3);
  28.     System.out.println(calc.evaluate("software * 2 + 1"));
  29.   }
  30. }

  31. /******************************************************************************
  32. 样例:使用下面的Solution类可以通过LeetCode 224

  33. class Solution {
  34.   private static Calculator calc = new Calculator();
  35.   static {
  36.     calc.registerOperation("+", 500, (a, b) -> a + b);
  37.     calc.registerOperation("-", 500, (a, b) -> a - b);
  38.     calc.registerOperation("-", 100, (a) -> -a);
  39.   }

  40.   public int calculate(String s) {
  41.     return calc.evaluate(s);
  42.   }
  43. }

  44. ******************************************************************************/

  45. /******************************************************************************
  46. 样例:使用下面的Solution类可以通过LeetCode 227

  47. class Solution {
  48.   private static Calculator calc = new Calculator();
  49.   static {
  50.     calc.registerOperation("+", 500, (a, b) -> a + b);
  51.     calc.registerOperation("-", 500, (a, b) -> a - b);
  52.     calc.registerOperation("*", 400, (a, b) -> a * b);
  53.     calc.registerOperation("/", 400, (a, b) -> a / b);
  54.     calc.registerOperation("-", 100, (a) -> -a);
  55.   }

  56.   public int calculate(String s) {
  57.     return calc.evaluate(s);
  58.   }
  59. }

  60. ******************************************************************************/

  61. class Calculator {
  62.   private Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  63.   private Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  64.   private Map<String, Function> functions;
  65.   private Map<String, Integer> environment;

  66.   public Calculator() {
  67.     this.unaryOperations =
  68.         new HashMap<String, OperationInfo<UnaryOperation>>();
  69.     this.binaryOperations =
  70.         new HashMap<String, OperationInfo<BinaryOperation>>();
  71.     this.functions = new HashMap<String, Function>();
  72.     this.environment = new HashMap<String, Integer>();
  73.   }

  74.   // 注册一个一元运算符
  75.   public void registerOperation(String operator, int precedence,
  76.                                 UnaryOperation operation) {
  77.     unaryOperations.put(
  78.         operator, new OperationInfo<UnaryOperation>(precedence, operation));
  79.   }

  80.   // 注册一个二元运算符
  81.   public void registerOperation(String operator, int precedence,
  82.                                 BinaryOperation operation) {
  83.     binaryOperations.put(
  84.         operator, new OperationInfo<BinaryOperation>(precedence, operation));
  85.   }

  86.   // 注册一个函数
  87.   public void registerFunction(String name, Function function) {
  88.     functions.put(name, function);
  89.   }

  90.   // 设置变量的值
  91.   public void setVariable(String name, int value) {
  92.     environment.put(name, value);
  93.   }

  94.   // 调用Executor对表达式进行计算
  95.   public int evaluate(String expression) {
  96.     CalculatorExecutor executor =
  97.         new CalculatorExecutor(unaryOperations, binaryOperations,
  98.                                functions, environment, expression);
  99.     return executor.run();
  100.   }
  101. }


  102. // 对一元运算符的抽象
  103. interface UnaryOperation {
  104.   public int apply(int operand);
  105. }

  106. // 对二元运算符的抽象
  107. interface BinaryOperation  {
  108.   public int apply(int lhs, int rhs);
  109. }

  110. // 对函数的抽象
  111. interface Function {
  112.   public int apply(int ...operands);
  113. }


  114. // 用于打包operation和对应的precedence信息的小类
  115. class OperationInfo<T> {
  116.   public final int precedence;
  117.   public final T operation;
  118.   public OperationInfo(int precedence, T operation) {
  119.     this.precedence = precedence;
  120.     this.operation = operation;
  121.   }
  122. }

  123. // LL(1) Recursive Descent Syntax Directed Translator
  124. class CalculatorExecutor {
  125.   private final Map<String, OperationInfo<UnaryOperation>> unaryOperations;
  126.   private final Map<String, OperationInfo<BinaryOperation>> binaryOperations;
  127.   private final Map<String, Function> functions;
  128.   private Map<String, Integer> environment;

  129.   private final String stream;
  130.   private int position;

  131.   // 词法部分也合并到这个类里了
  132.   private enum TokenType {
  133.     INTEGER,
  134.     NAME,
  135.     OPERATOR,
  136.     LPAREN,
  137.     RPAREN,
  138.     COMMA,
  139.     EOS,
  140.     ERROR
  141.   }

  142.   private class Token {
  143.     public final TokenType type;
  144.     public final Object payload;
  145.     public final int columnNumber;
  146.     public Token(TokenType type, int columnNumber) {
  147.       this(type, columnNumber, null);
  148.     }
  149.     public Token(TokenType type, int columnNumber, Object payload) {
  150.       this.type = type;
  151.       this.payload = payload;
  152.       this.columnNumber = columnNumber;
  153.     }
  154.   }

  155.   private Stack<Token> tokens;

  156.   public CalculatorExecutor(
  157.       Map<String, OperationInfo<UnaryOperation>> unaryOperations,
  158.       Map<String, OperationInfo<BinaryOperation>> binaryOperations,
  159.       Map<String, Function> functions,
  160.       Map<String, Integer> environment,
  161.       String stream) {
  162.     this.unaryOperations = unaryOperations;
  163.     this.binaryOperations = binaryOperations;
  164.     this.functions = functions;
  165.     this.environment = environment;
  166.     this.stream = stream;
  167.     this.position = 0;
  168.     this.tokens = new Stack<Token>();
  169.   }

  170.   public int run() {
  171.     int value = evalExpr(Integer.MAX_VALUE);
  172.     passNextToken(TokenType.EOS);
  173.     return value;
  174.   }

  175.   //////////////////////////////////////////////////////////////////////////////
  176.   // 语法处理部分

  177.   private int evalExpr(int precedence) {
  178.     int value = evalFactor(precedence);

  179.     // 二元运算符,注意这里对优先级的处理方法
  180.     while (hasNextToken(TokenType.OPERATOR)) {
  181.       Token token = nextToken();
  182.       OperationInfo<BinaryOperation> info =
  183.           binaryOperations.get((String)token.payload);
  184.       if (info == null || info.precedence >= precedence) {
  185.         pushBack(token);
  186.         break;
  187.       }
  188.       value = info.operation.apply(value, evalExpr(info.precedence));
  189.     }
  190.     return value;
  191.   }

  192.   private int evalFactor(int precedence) {
  193.     Token token = nextToken();

  194.     // Case 1. 数字自身
  195.     if (token.type == TokenType.INTEGER) {
  196.       return (Integer)token.payload;
  197.     }

  198.     // Case 2. 一元运算符
  199.     if (token.type == TokenType.OPERATOR) {
  200.       OperationInfo<UnaryOperation> info =
  201.           unaryOperations.get((String)token.payload);
  202.       if (info == null || info.precedence > precedence) {
  203.         error(token);
  204.       }
  205.       return info.operation.apply(evalExpr(info.precedence));
  206.     }

  207.     // Case 3. 函数调用或者变量
  208.     if (token.type == TokenType.NAME) {
  209.       if (hasNextToken(TokenType.LPAREN)) {
  210.         Function func = functions.get((String)token.payload);
  211.         if (func == null) {
  212.           error(token);
  213.         }
  214.         ArrayList<Integer> args = new ArrayList<Integer>();
  215.         passNextToken(TokenType.LPAREN);
  216.         while (!hasNextToken(TokenType.RPAREN)) {
  217.           args.add(evalExpr(Integer.MAX_VALUE));
  218.           if (!hasNextToken(TokenType.COMMA)) {
  219.             break;
  220.           }
  221.           nextToken();
  222.         }
  223.         passNextToken(TokenType.RPAREN);
  224.         return func.apply(args.stream().mapToInt(x -> x).toArray());
  225.       } else {
  226.         String variable = (String)token.payload;
  227.         Integer value = environment.get(variable);
  228.         if (value == null) {
  229.           throw new RuntimeException(
  230.               "Undefined symbol " + variable + " at column " + token.columnNumber);
  231.         }
  232.         return value;
  233.       }
  234.     }

  235.     // Case 4. 括号表达式
  236.     if (token.type == TokenType.LPAREN) {
  237.       int value = evalExpr(Integer.MAX_VALUE);
  238.       Token rp = nextToken();
  239.       if (rp.type != TokenType.RPAREN) {
  240.         error(token);
  241.       }
  242.       return value;
  243.     }

  244.     error(token);
  245.     return 0;
  246.   }

  247.   //////////////////////////////////////////////////////////////////////////////
  248.   // 词法处理部分

  249.   private boolean hasNextToken(TokenType ...expected) {
  250.     Token token = nextToken();
  251.     boolean status = Arrays.stream(expected).anyMatch(x -> x == token.type);
  252.     pushBack(token);
  253.     return status;
  254.   }

  255.   private void passNextToken(TokenType ...expected) {
  256.     Token token = nextToken();
  257.     if (!Arrays.stream(expected).anyMatch(x -> x == token.type)) {
  258.       error(token);
  259.     }
  260.   }

  261.   private void pushBack(Token token) {
  262.     tokens.add(token);
  263.   }

  264.   private Token nextToken() {
  265.     if (!tokens.isEmpty()) {
  266.       return tokens.pop();
  267.     }
  268.     while (position < stream.length() &&
  269.            Character.isWhitespace(stream.charAt(position))) {
  270.       ++position;
  271.     }

  272.     if (position >= stream.length()) {
  273.       return new Token(TokenType.EOS, position);
  274.     }

  275.     final int start = position;
  276.     final char ch = stream.charAt(position++);

  277.     switch (ch) {
  278.       case '(': return new Token(TokenType.LPAREN, start);
  279.       case ')': return new Token(TokenType.RPAREN, start);
  280.       case ',': return new Token(TokenType.COMMA, start);
  281.       default:  --position; break;
  282.     }

  283.     if (Character.isDigit(ch)) {
  284.       return nextNumber();
  285.     }
  286.     if (Character.isAlphabetic(ch)) {
  287.       return nextName();
  288.     }
  289.     return nextOperator();
  290.   }

  291.   private Token nextNumber() {
  292.     final int start = position;
  293.     while (position < stream.length() &&
  294.            Character.isDigit(stream.charAt(position))) {
  295.       ++position;
  296.     }
  297.     return new Token(TokenType.INTEGER, start,
  298.                      Integer.parseInt(stream.substring(start, position)));
  299.   }

  300.   private Token nextName() {
  301.     final int start = position;
  302.     while (position < stream.length() &&
  303.            Character.isAlphabetic(stream.charAt(position))) {
  304.       ++position;
  305.     }
  306.     return new Token(TokenType.NAME, start,
  307.                      stream.substring(start, position));
  308.   }

  309.   private Token nextOperator() {
  310.     final int start = position;
  311.     while (position < stream.length()) {
  312.       final char ch = stream.charAt(position);
  313.       if (Character.isWhitespace(ch) ||
  314.           Character.isDigit(ch) ||
  315.           Character.isAlphabetic(ch) ||
  316.           ch == '(' || ch == ')' || ch == ',') {
  317.         break;
  318.       }
  319.       ++position;
  320.     }
  321.     return new Token(TokenType.OPERATOR, start,
  322.                      stream.substring(start, position));
  323.   }

  324.   private void error(Token token) {
  325.     throw new RuntimeException(
  326.         "Syntax error: unexpected token at column " +
  327.         (token.columnNumber+1) + " -- " + token.type.name() +
  328.         (token.payload == null ? "" : " (" + token.payload.toString() + ")"));
  329.   }
  330. }
复制代码

评分

参与人数 2大米 +11 收起 理由
Killua1222 + 3 感激楼主
biomedicineman + 8 很有用的信息!

查看全部评分

回复

使用道具 举报

全局:
intuit面试calculator题followup 3, 要求带入variable

比如 given:
String s = "software  * 2 + 1"
Map: {"software" : 3}

那么需要求出3 * 2 + 1 = 7
回复

使用道具 举报

全局:
magicsets 发表于 2018-1-12 13:29
这个需要维护一个运行时环境(Runtime Environment),大概添加20行代码可以实现,参考下面代码的第34、3 ...

赞赞赞。

其实我刚才没说完。
intuit当时还有一个要求

比如String = "software + 2 * 3";
Map: {"hardware” : 5}

也就是说会出现map里没有对应key的情况,那么就把能计算的算了,然后输出string
"software + 6"
回复

使用道具 举报

🔗
 楼主| magicsets 2018-1-12 13:53:29 | 只看该作者
全局:
biomedicineman 发表于 2018-1-12 13:33
赞赞赞。

其实我刚才没说完。

这题目出得很偏啊 @_@,这个功能上和Calculator不是一回事了

比较系统的解决方案是生成语法树(Abstract Syntax Tree),然后求值所有不包含未定义变量的子树,然后再将折叠后的语法树输出为表达式

如果是改动我之前贴的代码的话,也可以进一步添加类型系统(Type System)以支持字符串和整数两种类型,这样的话诸如evalExpr()的返回值就不是int了,而是一个value union(常常写做TypedValue),类似于python object那种泛型变量。

然后求值时找不到变量的话就返回变量名的字符串;支持隐式类型转换以处理字符串和数字的运算。有点麻烦我就不写了...

评分

参与人数 1大米 +6 收起 理由
biomedicineman + 6 给你点个赞!

查看全部评分

回复

使用道具 举报

🔗
 楼主| magicsets 2018-1-12 14:04:38 | 只看该作者
全局:
biomedicineman 发表于 2018-1-12 13:33
赞赞赞。

其实我刚才没说完。

谢谢大米

评分

参与人数 1大米 +25 收起 理由
忆梦前尘 + 25 很有用的信息!

查看全部评分

回复

使用道具 举报

全局:
magicsets 发表于 2018-1-12 13:53
这题目出得很偏啊 @_@,这个功能上和Calculator不是一回事了

比较系统的解决方案是生成语法树(Abstra ...

是啊是啊。最后那种情况压根就不是计算器了。。。所以我最后这个面试的时候没做出来。。。至今也没时间figure out
回复

使用道具 举报

🔗
huorili 2020-1-29 21:01:56 | 只看该作者
全局:
这个不给大米说不过去啊。。。太牛了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表