java实现表达式计算
功能特点:
-
灵活的函数注册:
-
固定参数函数:
registerFunction("sqrt", 1, ...)
-
可变参数函数:
registerFunction("max", -1, ...)
-
-
丰富的内置函数:
-
数学函数:sin、cos、sqrt
-
统计函数:max(可自动扩展)
-
-
严谨的错误处理:
-
未定义变量检测
-
除零错误检测
-
参数个数校验
-
括号匹配检查
-
-
高性能设计:
-
采用逆波兰表达式算法
-
单次解析多次求值
-
线程安全设计
-
该实现通过扩展Tokenizer识别函数调用,改进Parser处理参数列表,并增加函数注册机制,使工具能够灵活支持各种数学函数的扩展需求。
import java.util.*;
import java.util.function.Function;
public class ExpressionCalculator {
public static void main(String[] args) {
// 使用示例
Expression expr = new Expression("sin(π/2) + max(3, 5, 2)");
Map<String, Double> variables = new HashMap<>();
variables.put("π", Math.PI);
try {
double result = expr.evaluate(variables);
System.out.println("计算结果: " + result); // 输出: 1.0 + 5 = 6.0
} catch (Exception e) {
e.printStackTrace();
}
Expression expr2 = new Expression("sin(x) + log(y, 10)");
Map<String, Double> variables2 = new HashMap<>();
variables2.put("x", Math.PI/2);
variables2.put("y", 100.0);
// 注册log函数(以10为底)
double result = expr2.evaluate(variables2); // sin(π/2)=1, log10(100)=2 → 结果=3
System.out.println(result);
}
static class Expression {
private final List<Token> postfixTokens;
private static final Map<String, RegisteredFunction> functions = new HashMap<>();
static {
// 注册内置函数
registerFunction("sin", 1, args -> Math.sin(args[0]));
registerFunction("cos", 1, args -> Math.cos(args[0]));
registerFunction("sqrt", 1, args -> {
if (args[0] < 0) throw new ArithmeticException("负数开平方");
return Math.sqrt(args[0]);
});
registerFunction("max", -1, args ->
Arrays.stream(args)
.mapToDouble(Double::doubleValue)
.max()
.orElseThrow(() -> new ArithmeticException("至少需要一个参数"))
);
// 可选的min函数修正
registerFunction("min", -1, args ->
Arrays.stream(args)
.mapToDouble(Double::doubleValue)
.min()
.orElseThrow(() -> new ArithmeticException("至少需要一个参数"))
);
registerFunction("log", 2, args -> Math.log10(args[0])/Math.log10(args[1]));
}
public static void registerFunction(String name, int argCount, Function<Double[], Double> function) {
functions.put(name, new RegisteredFunction(argCount, function));
}
public Expression(String expression) {
Tokenizer tokenizer = new Tokenizer(expression);
List<Token> infixTokens = tokenizer.tokenize();
this.postfixTokens = Parser.infixToPostfix(infixTokens);
}
public double evaluate(Map<String, Double> variables) {
Deque<Double> stack = new ArrayDeque<>();
for (Token token : postfixTokens) {
switch (token.type) {
case NUMBER:
stack.push(token.number);
break;
case VARIABLE:
handleVariable(token, stack, variables);
break;
case FUNCTION:
handleFunction(token, stack);
break;
case OPERATOR:
handleOperator(token.operator, stack);
break;
default:
throw new IllegalStateException("意外的token类型");
}
}
if (stack.size() != 1) {
throw new ArithmeticException("无效的表达式");
}
return stack.pop();
}
private void handleVariable(Token token, Deque<Double> stack, Map<String, Double> vars) {
String varName = token.variable;
if (!vars.containsKey(varName)) {
throw new IllegalArgumentException("未定义变量: " + varName);
}
stack.push(vars.get(varName));
}
private void handleFunction(Token token, Deque<Double> stack) {
String funcName = token.variable;
int argCount = token.argumentCount;
RegisteredFunction func = functions.get(funcName);
if (func == null) throw new IllegalArgumentException("未注册函数: " + funcName);
if (func.argCount != -1 && argCount != func.argCount) {
throw new IllegalArgumentException("参数个数错误: " + funcName);
}
Double[] args = new Double[argCount];
for (int i = argCount-1; i >= 0; i--) {
args[i] = stack.pop();
}
stack.push(func.function.apply(args));
}
private void handleOperator(char op, Deque<Double> stack) {
if (op == 'u') {
stack.push(-stack.pop());
} else {
double b = stack.pop(), a = stack.pop();
switch (op) {
case '+': stack.push(a + b); break;
case '-': stack.push(a - b); break;
case '*': stack.push(a * b); break;
case '/':
if (b == 0) throw new ArithmeticException("除零错误");
stack.push(a / b);
break;
case '^': stack.push(Math.pow(a, b)); break;
default: throw new IllegalArgumentException("无效运算符");
}
}
}
private static class RegisteredFunction {
int argCount; // -1表示可变参数
Function<Double[], Double> function;
RegisteredFunction(int argCount, Function<Double[], Double> function) {
this.argCount = argCount;
this.function = function;
}
}
}
// 以下是Tokenizer、Parser和Token类的实现(改进版)
enum TokenType { NUMBER, VARIABLE, FUNCTION, OPERATOR, LEFT_PAREN, RIGHT_PAREN, COMMA }
static class Token {
final TokenType type;
Double number;
String variable;
Character operator;
int argumentCount;
private Token(TokenType type) { this.type = type; }
static Token number(double value) {
Token t = new Token(TokenType.NUMBER);
t.number = value;
return t;
}
static Token variable(String name) {
Token t = new Token(TokenType.VARIABLE);
t.variable = name;
return t;
}
static Token function(String name, int argCount) {
Token t = new Token(TokenType.FUNCTION);
t.variable = name;
t.argumentCount = argCount;
return t;
}
static Token operator(char op) {
Token t = new Token(TokenType.OPERATOR);
t.operator = op;
return t;
}
static Token leftParen() { return new Token(TokenType.LEFT_PAREN); }
static Token rightParen() { return new Token(TokenType.RIGHT_PAREN); }
static Token comma() { return new Token(TokenType.COMMA); }
}
static class Tokenizer {
private final String input;
private int pos = 0;
private char currentChar;
Tokenizer(String input) {
this.input = input.replaceAll("\\s+", "");
this.currentChar = !this.input.isEmpty() ? this.input.charAt(0) : '\0';
}
List<Token> tokenize() {
List<Token> tokens = new ArrayList<>();
while (currentChar != '\0') {
if (Character.isDigit(currentChar) || currentChar == '.') {
tokens.add(readNumber());
} else if (Character.isLetter(currentChar)) {
tokens.add(readIdentifier());
} else if ("+-*/^".indexOf(currentChar) != -1) {
tokens.add(readOperator());
} else if (currentChar == '(') {
tokens.add(Token.leftParen());
advance();
} else if (currentChar == ')') {
tokens.add(Token.rightParen());
advance();
} else if (currentChar == ',') {
tokens.add(Token.comma());
advance();
} else {
throw new RuntimeException("无效字符: " + currentChar);
}
}
return tokens;
}
private Token readNumber() {
StringBuilder sb = new StringBuilder();
boolean hasDecimal = false;
while (currentChar != '\0' && (Character.isDigit(currentChar) || currentChar == '.')) {
if (currentChar == '.') {
if (hasDecimal) throw new RuntimeException("无效数字格式");
hasDecimal = true;
}
sb.append(currentChar);
advance();
}
try {
return Token.number(Double.parseDouble(sb.toString()));
} catch (NumberFormatException e) {
throw new RuntimeException("无效数字: " + sb);
}
}
private Token readIdentifier() {
StringBuilder sb = new StringBuilder();
while (currentChar != '\0' && Character.isLetterOrDigit(currentChar)) {
sb.append(currentChar);
advance();
}
if (currentChar == '(') {
return Token.function(sb.toString(), 0);
} else {
return Token.variable(sb.toString());
}
}
private Token readOperator() {
char op = currentChar;
advance();
return Token.operator(op);
}
private void advance() {
pos++;
currentChar = (pos < input.length()) ? input.charAt(pos) : '\0';
}
}
static class Parser {
static List<Token> infixToPostfix(List<Token> infixTokens) {
List<Token> output = new ArrayList<>();
Deque<Token> stack = new ArrayDeque<>();
Deque<Integer> argCountStack = new ArrayDeque<>();
for (Token token : infixTokens) {
switch (token.type) {
case NUMBER: case VARIABLE:
output.add(token); break;
case FUNCTION:
stack.push(token);
argCountStack.push(1);
break;
case LEFT_PAREN:
stack.push(token); break;
case COMMA:
handleComma(output, stack, argCountStack); break;
case RIGHT_PAREN:
handleRightParen(output, stack, argCountStack); break;
case OPERATOR:
handleOperator(token, output, stack); break;
}
}
while (!stack.isEmpty()) {
Token t = stack.pop();
if (t.type == TokenType.LEFT_PAREN) throw new RuntimeException("括号不匹配");
output.add(t);
}
return output;
}
private static void handleComma(List<Token> output, Deque<Token> stack, Deque<Integer> argCountStack) {
while (!stack.isEmpty() && stack.peek().type != TokenType.LEFT_PAREN) {
output.add(stack.pop());
}
if (!argCountStack.isEmpty()) {
argCountStack.push(argCountStack.pop() + 1);
}
}
private static void handleRightParen(List<Token> output, Deque<Token> stack, Deque<Integer> argCountStack) {
while (!stack.isEmpty() && stack.peek().type != TokenType.LEFT_PAREN) {
output.add(stack.pop());
}
if (stack.isEmpty()) throw new RuntimeException("括号不匹配");
stack.pop(); // 移除左括号
if (!stack.isEmpty() && stack.peek().type == TokenType.FUNCTION) {
Token func = stack.pop();
int args = argCountStack.pop();
output.add(Token.function(func.variable, args));
}
}
private static void handleOperator(Token token, List<Token> output, Deque<Token> stack) {
while (!stack.isEmpty() && precedence(stack.peek().operator) >= precedence(token.operator)) {
output.add(stack.pop());
}
stack.push(token);
}
private static int precedence(Character op) {
if (op == null) return -1;
switch (op) {
case '^': return 4;
case '*': case '/': return 3;
case '+': case '-': return 2;
default: return -1;
}
}
}
}