You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pairedProject/src/main/java/com/ybw/mathapp/service/AdvancedCaculate.java

180 lines
6.6 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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<String> OPERATORS = new HashSet<>(Arrays.asList("+", "-", "*", "/"));
private static final Set<String> ADVANCED_OPERATORS = new HashSet<>(Arrays.asList("平方", "开根号", "sin", "cos", "tan"));
private static final Map<String, Integer> 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<String> expressionTokens) {
Stack<Double> numberStack = new Stack<>();
Stack<String> 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<Double> 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;
}
}
}