package com.ybw.mathapp.service; import java.util.*; /** * 扩展的计算类,支持基础四则运算、括号、平方、开根号、三角函数 (sin, cos, tan)。 * 括号优先级最高。 * 高级运算符 (平方, 开根号, sin, cos, tan) 优先级高于四则运算。 * 平方是后置运算符,开根号和三角函数是前置运算符。 * 例如: "3 + 开根号 16 平方" 解释为 "3 + (sqrt(16))^2" = "3 + 4^2" = "3 + 16" = 19 * "开根号 ( 4 + 5 ) 平方" 解释为 "(sqrt(4+5))^2" = "sqrt(9)^2" = "3^2" = 9 * "sin 30 + 5" 解释为 "sin(30) + 5" (假设30是度数) * 修正:在计算开根号时,若操作数为负数,则抛出 ArithmeticException。 */ public class AdvancedCaculate { private static final Set OPERATORS = new HashSet<>(Arrays.asList("+", "-", "*", "/")); private static final Set ADVANCED_OPERATORS = new HashSet<>(Arrays.asList("平方", "开根号", "sin", "cos", "tan")); private static final Map PRECEDENCE = new HashMap<>(); static { PRECEDENCE.put("+", 1); PRECEDENCE.put("-", 1); PRECEDENCE.put("*", 2); PRECEDENCE.put("/", 2); // 高级运算符优先级更高 PRECEDENCE.put("平方", 3); // 后置 PRECEDENCE.put("开根号", 3); // 前置 PRECEDENCE.put("sin", 3); // 前置 PRECEDENCE.put("cos", 3); // 前置 PRECEDENCE.put("tan", 3); // 前置 } /** * 计算给定表达式的值。 * * @param expressionTokens 表达式分词列表,例如 ["3", "+", "开根号", "16", "平方"] * @return 计算结果 * @throws IllegalArgumentException 如果表达式无效 * @throws ArithmeticException 如果计算过程中出现错误(如除零、负数开根号) */ public static double calculate(List expressionTokens) { Stack numberStack = new Stack<>(); Stack operatorStack = new Stack<>(); for (int i = 0; i < expressionTokens.size(); i++) { String token = expressionTokens.get(i); if (isNumeric(token)) { numberStack.push(Double.parseDouble(token)); } else if (token.equals("(")) { operatorStack.push(token); } else if (token.equals(")")) { while (!operatorStack.isEmpty() && !operatorStack.peek().equals("(")) { processOperator(numberStack, operatorStack.pop()); } if (!operatorStack.isEmpty()) { // Pop the "(" operatorStack.pop(); } } else if (ADVANCED_OPERATORS.contains(token)) { // 前置运算符 (开根号, sin, cos, tan) 直接入栈 // 后置运算符 (平方) 需要等待其操作数先计算出来 // 在标准调度场算法中,后置运算符通常在遇到时立即处理其栈顶的操作数 // 这里我们可以在遇到 "平方" 时,立即对 numberStack 的顶部元素进行平方操作 if ("平方".equals(token)) { if (numberStack.isEmpty()) { throw new IllegalArgumentException("Invalid expression: '平方' lacks an operand."); } double operand = numberStack.pop(); numberStack.push(Math.pow(operand, 2)); } else { // "开根号", "sin", "cos", "tan" 是前置运算符 operatorStack.push(token); } } else if (OPERATORS.contains(token)) { // 处理四则运算符,遵循优先级 while (!operatorStack.isEmpty() && !operatorStack.peek().equals("(") && PRECEDENCE.get(token) <= PRECEDENCE.getOrDefault(operatorStack.peek(), 0)) { processOperator(numberStack, operatorStack.pop()); } operatorStack.push(token); } else { throw new IllegalArgumentException("Unknown token: " + token); } } // 处理栈中剩余的所有运算符 while (!operatorStack.isEmpty()) { String op = operatorStack.pop(); if (op.equals("(") || op.equals(")")) { throw new IllegalArgumentException("Mismatched parentheses in expression."); } processOperator(numberStack, op); } if (numberStack.size() != 1 || !operatorStack.isEmpty()) { throw new IllegalArgumentException("Invalid expression: " + String.join(" ", expressionTokens)); } return numberStack.pop(); } /** * 执行一次运算操作。 * * @param numberStack 数字栈 * @param operator 运算符 */ private static void processOperator(Stack numberStack, String operator) { if (numberStack.size() < 1) { throw new IllegalArgumentException("Invalid expression: operator '" + operator + "' lacks operand(s)."); } if (ADVANCED_OPERATORS.contains(operator)) { double operand = numberStack.pop(); switch (operator) { case "开根号": if (operand < 0) { // 抛出异常,让调用者(MultipleChoiceGenerator)处理 throw new ArithmeticException("Cannot take square root of negative number: " + operand); } numberStack.push(Math.sqrt(operand)); break; case "sin": numberStack.push(Math.sin(Math.toRadians(operand))); // 假设输入是度数 break; case "cos": numberStack.push(Math.cos(Math.toRadians(operand))); break; case "tan": // tan(90 + n*180) 会趋向无穷,这里不特别处理,让其返回 Infinity 或 -Infinity numberStack.push(Math.tan(Math.toRadians(operand))); break; default: throw new IllegalArgumentException("Unknown advanced operator: " + operator); } } else if (OPERATORS.contains(operator)) { if (numberStack.size() < 2) { throw new IllegalArgumentException("Invalid expression: operator '" + operator + "' lacks operand(s)."); } double b = numberStack.pop(); double a = numberStack.pop(); double result; switch (operator) { case "+": result = a + b; break; case "-": result = a - b; break; case "*": result = a * b; break; case "/": if (b == 0) { throw new ArithmeticException("Division by zero"); } result = a / b; break; default: throw new IllegalArgumentException("Unknown operator: " + operator); } numberStack.push(result); } else { throw new IllegalArgumentException("Unexpected operator in process: " + operator); } } public static boolean isNumeric(String str) { if (str == null || str.isEmpty()) { return false; } try { Double.parseDouble(str); return true; } catch (NumberFormatException e) { return false; } } }