java如何计算算术表达式

java如何计算算术表达式

Java如何计算算术表达式主要涉及到使用栈数据结构、解析表达式、使用现有库函数。其中,使用栈数据结构是计算算术表达式中最常用且高效的方法。接下来,我们将详细展开其中一种方法,并介绍其他的技巧和工具来实现这一目标。

一、使用栈数据结构

使用栈数据结构来计算算术表达式是一种经典的方法。栈是一种后进先出(LIFO)的数据结构,非常适合处理括号和操作符的优先级问题。我们可以使用两个栈,一个存放操作数,一个存放操作符。

1.1 中缀表达式转后缀表达式

中缀表达式是我们日常使用的表达式形式,比如 3 + 5 * 2。为了方便计算,我们需要将其转换为后缀表达式(逆波兰表示法),比如 3 5 2 * +。在转换过程中,我们利用栈来处理操作符的优先级。

import java.util.Stack;

public class InfixToPostfix {

public static int precedence(char ch) {

switch (ch) {

case '+':

case '-':

return 1;

case '*':

case '/':

return 2;

case '^':

return 3;

}

return -1;

}

public static String infixToPostfix(String expression) {

StringBuilder result = new StringBuilder();

Stack<Character> stack = new Stack<>();

for (int i = 0; i < expression.length(); ++i) {

char c = expression.charAt(i);

// If the character is an operand, add it to the output

if (Character.isLetterOrDigit(c))

result.append(c);

// If the character is '(', push it to the stack

else if (c == '(')

stack.push(c);

// If the character is ')', pop and output from the stack

// until an '(' is encountered

else if (c == ')') {

while (!stack.isEmpty() && stack.peek() != '(')

result.append(stack.pop());

stack.pop();

} else { // an operator is encountered

while (!stack.isEmpty() && precedence(c) <= precedence(stack.peek())) {

result.append(stack.pop());

}

stack.push(c);

}

}

// pop all the operators from the stack

while (!stack.isEmpty()) {

result.append(stack.pop());

}

return result.toString();

}

public static void main(String[] args) {

String expression = "3+5*2/(7-2)";

System.out.println(infixToPostfix(expression));

}

}

1.2 计算后缀表达式

一旦我们得到了后缀表达式,计算它就变得相对简单了。我们再次使用一个栈来存储操作数,遇到操作符时,弹出栈顶的两个操作数,进行相应的计算,并将结果压回栈中。

import java.util.Stack;

public class PostfixEvaluation {

public static int evaluatePostfix(String expression) {

Stack<Integer> stack = new Stack<>();

for (int i = 0; i < expression.length(); i++) {

char c = expression.charAt(i);

if (Character.isDigit(c))

stack.push(c - '0');

else {

int val1 = stack.pop();

int val2 = stack.pop();

switch (c) {

case '+':

stack.push(val2 + val1);

break;

case '-':

stack.push(val2 - val1);

break;

case '*':

stack.push(val2 * val1);

break;

case '/':

stack.push(val2 / val1);

break;

}

}

}

return stack.pop();

}

public static void main(String[] args) {

String postfixExpression = "352*+72-/";

System.out.println(evaluatePostfix(postfixExpression));

}

}

二、使用现有库函数

Java中也有许多现成的库函数可以帮助我们计算算术表达式。常见的库有 JEXL、MVEL 等,这些库提供了丰富的功能,可以解析和计算复杂的表达式。

2.1 使用 JEXL 库

Apache Commons JEXL 是一个表达式语言库,支持动态脚本编写和解释。我们可以使用它来计算算术表达式。

首先,添加 JEXL 依赖:

<dependency>

<groupId>org.apache.commons</groupId>

<artifactId>commons-jexl3</artifactId>

<version>3.2</version>

</dependency>

然后,可以通过以下代码来计算表达式:

import org.apache.commons.jexl3.JexlBuilder;

import org.apache.commons.jexl3.JexlEngine;

import org.apache.commons.jexl3.JexlExpression;

import org.apache.commons.jexl3.MapContext;

public class JexlExample {

public static void main(String[] args) {

JexlEngine jexl = new JexlBuilder().create();

String expression = "3 + 5 * 2 / (7 - 2)";

JexlExpression e = jexl.createExpression(expression);

Object result = e.evaluate(new MapContext());

System.out.println(result);

}

}

2.2 使用 MVEL 库

MVEL 是一个功能强大的表达式语言库,它同样可以用于计算算术表达式。首先,添加 MVEL 依赖:

<dependency>

<groupId>org.mvel</groupId>

<artifactId>mvel2</artifactId>

<version>2.4.7.Final</version>

</dependency>

然后,可以通过以下代码来计算表达式:

import org.mvel2.MVEL;

public class MvelExample {

public static void main(String[] args) {

String expression = "3 + 5 * 2 / (7 - 2)";

Object result = MVEL.eval(expression);

System.out.println(result);

}

}

三、解析表达式

解析表达式是计算算术表达式的关键步骤,解析过程包括词法分析和语法分析。通过解析,我们可以将表达式分解成操作数和操作符,并按优先级进行计算。

3.1 词法分析

词法分析的目的是将表达式分解成一个个记号(token),每个记号可以是操作数、操作符或括号。下面是一个简单的词法分析器:

import java.util.ArrayList;

import java.util.List;

public class Lexer {

public static List<String> tokenize(String expression) {

List<String> tokens = new ArrayList<>();

int i = 0;

while (i < expression.length()) {

char c = expression.charAt(i);

if (Character.isDigit(c)) {

StringBuilder sb = new StringBuilder();

while (i < expression.length() && Character.isDigit(expression.charAt(i))) {

sb.append(expression.charAt(i++));

}

tokens.add(sb.toString());

} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {

tokens.add(Character.toString(c));

i++;

} else {

i++;

}

}

return tokens;

}

public static void main(String[] args) {

String expression = "3 + 5 * 2 / (7 - 2)";

List<String> tokens = tokenize(expression);

for (String token : tokens) {

System.out.println(token);

}

}

}

3.2 语法分析

语法分析的目的是根据记号生成一个语法树,然后根据语法树进行计算。下面是一个简单的语法分析器:

import java.util.List;

import java.util.Stack;

public class Parser {

public static int parse(List<String> tokens) {

Stack<Integer> values = new Stack<>();

Stack<Character> ops = new Stack<>();

for (String token : tokens) {

if (Character.isDigit(token.charAt(0))) {

values.push(Integer.parseInt(token));

} else if (token.charAt(0) == '(') {

ops.push('(');

} else if (token.charAt(0) == ')') {

while (ops.peek() != '(') {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.pop();

} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {

while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.push(token.charAt(0));

}

}

while (!ops.isEmpty()) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

return values.pop();

}

public static boolean hasPrecedence(char op1, char op2) {

if (op2 == '(' || op2 == ')')

return false;

if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))

return false;

return true;

}

public static int applyOp(char op, int b, int a) {

switch (op) {

case '+':

return a + b;

case '-':

return a - b;

case '*':

return a * b;

case '/':

if (b == 0)

throw new UnsupportedOperationException("Cannot divide by zero");

return a / b;

}

return 0;

}

public static void main(String[] args) {

String expression = "3 + 5 * 2 / (7 - 2)";

List<String> tokens = Lexer.tokenize(expression);

System.out.println(parse(tokens));

}

}

四、扩展功能

除了基础的算术运算,我们还可以扩展更多的功能,比如支持变量、函数调用、数学库函数等。

4.1 支持变量

为了支持变量,我们需要在解析和计算过程中引入一个上下文(context),上下文存储了变量的值。

import java.util.HashMap;

import java.util.Map;

import java.util.Stack;

public class VariableSupport {

private static Map<String, Integer> context = new HashMap<>();

public static int parse(List<String> tokens) {

Stack<Integer> values = new Stack<>();

Stack<Character> ops = new Stack<>();

for (String token : tokens) {

if (Character.isDigit(token.charAt(0))) {

values.push(Integer.parseInt(token));

} else if (context.containsKey(token)) {

values.push(context.get(token));

} else if (token.charAt(0) == '(') {

ops.push('(');

} else if (token.charAt(0) == ')') {

while (ops.peek() != '(') {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.pop();

} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {

while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.push(token.charAt(0));

}

}

while (!ops.isEmpty()) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

return values.pop();

}

public static boolean hasPrecedence(char op1, char op2) {

if (op2 == '(' || op2 == ')')

return false;

if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))

return false;

return true;

}

public static int applyOp(char op, int b, int a) {

switch (op) {

case '+':

return a + b;

case '-':

return a - b;

case '*':

return a * b;

case '/':

if (b == 0)

throw new UnsupportedOperationException("Cannot divide by zero");

return a / b;

}

return 0;

}

public static void main(String[] args) {

context.put("x", 5);

context.put("y", 2);

String expression = "3 + x * y / (7 - 2)";

List<String> tokens = Lexer.tokenize(expression);

System.out.println(parse(tokens));

}

}

4.2 支持函数调用

为了支持函数调用,我们需要在上下文中存储函数定义,并在解析和计算过程中进行相应的调用。

import java.util.HashMap;

import java.util.Map;

import java.util.Stack;

import java.util.function.Function;

public class FunctionSupport {

private static Map<String, Integer> context = new HashMap<>();

private static Map<String, Function<Integer[], Integer>> functions = new HashMap<>();

public static int parse(List<String> tokens) {

Stack<Integer> values = new Stack<>();

Stack<Character> ops = new Stack<>();

for (String token : tokens) {

if (Character.isDigit(token.charAt(0))) {

values.push(Integer.parseInt(token));

} else if (context.containsKey(token)) {

values.push(context.get(token));

} else if (functions.containsKey(token)) {

// handle function call

} else if (token.charAt(0) == '(') {

ops.push('(');

} else if (token.charAt(0) == ')') {

while (ops.peek() != '(') {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.pop();

} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {

while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

ops.push(token.charAt(0));

}

}

while (!ops.isEmpty()) {

values.push(applyOp(ops.pop(), values.pop(), values.pop()));

}

return values.pop();

}

public static boolean hasPrecedence(char op1, char op2) {

if (op2 == '(' || op2 == ')')

return false;

if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))

return false;

return true;

}

public static int applyOp(char op, int b, int a) {

switch (op) {

case '+':

return a + b;

case '-':

return a - b;

case '*':

return a * b;

case '/':

if (b == 0)

throw new UnsupportedOperationException("Cannot divide by zero");

return a / b;

}

return 0;

}

public static void main(String[] args) {

context.put("x", 5);

context.put("y", 2);

functions.put("max", args -> Math.max(args[0], args[1]));

String expression = "3 + max(x, y) * y / (7 - 2)";

List<String> tokens = Lexer.tokenize(expression);

System.out.println(parse(tokens));

}

}

总结

在本文中,我们探讨了在Java中计算算术表达式的多种方法,包括使用栈数据结构、使用现有库函数以及解析表达式。每种方法都有其优缺点,选择哪种方法取决于具体的需求和场景。

使用栈数据结构适合处理简单的算术表达式,能够清晰地处理操作符优先级和括号。使用现有库函数如JEXL和MVEL,可以快速实现复杂表达式的解析和计算,适合需要动态解析和计算表达式的场景。解析表达式的方法适合需要扩展功能的场景,如支持变量、函数调用等。通过这些方法,我们可以灵活地处理各种算术表达式的计算需求。

相关问答FAQs:

1. 如何在Java中计算算术表达式?

Java提供了一个强大的表达式求值引擎,可以用于计算算术表达式。您可以使用Java的内置库,例如ScriptEngine,来实现这一点。首先,您需要导入javax.script包,然后创建一个ScriptEngine对象。接下来,您可以使用ScriptEngine的eval()方法来计算算术表达式。例如:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ArithmeticExpressionCalculator {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        try {
            String expression = "2 + 3 * 4";
            Object result = engine.eval(expression);
            System.out.println("计算结果: " + result);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们使用JavaScript引擎来计算算术表达式"2 + 3 * 4"。eval()方法返回一个Object类型的结果,您可以根据需要进行类型转换。

2. 如何处理带有变量的算术表达式?

如果您的算术表达式包含变量,您可以使用ScriptEngine的Bindings来传递变量值。首先,您需要创建一个Bindings对象,然后将变量名和值绑定到Bindings中。接下来,您可以使用ScriptEngine的setBindings()方法将Bindings对象传递给引擎。这样,您就可以在表达式中使用这些变量了。例如:

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class VariableExpressionCalculator {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        try {
            Bindings bindings = engine.createBindings();
            bindings.put("x", 2);
            bindings.put("y", 3);
            String expression = "x + y * 4";
            Object result = engine.eval(expression, bindings);
            System.out.println("计算结果: " + result);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们创建了两个变量x和y,并将它们的值分别设置为2和3。然后,我们在表达式"x + y * 4"中使用了这些变量,并通过Bindings对象传递给ScriptEngine。

3. 如何处理复杂的算术表达式,例如带有括号和函数调用?

如果您的算术表达式比较复杂,例如包含括号和函数调用,您可以使用ScriptEngine的更多功能来处理。首先,您可以使用括号来明确指定运算的优先级。例如,"(2 + 3) * 4"将先计算括号内的表达式,然后再乘以4。其次,您可以使用JavaScript的内置数学函数,例如Math.sqrt()来计算平方根,Math.sin()来计算正弦值等等。例如:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ComplexExpressionCalculator {
    public static void main(String[] args) {
        ScriptEngineManager manager = new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");

        try {
            String expression = "(2 + 3) * Math.sqrt(16)";
            Object result = engine.eval(expression);
            System.out.println("计算结果: " + result);
        } catch (ScriptException e) {
            e.printStackTrace();
        }
    }
}

在上面的示例中,我们计算了表达式"(2 + 3) * Math.sqrt(16)",其中括号内的表达式先计算,然后再乘以16的平方根。您可以根据需要使用其他JavaScript内置函数来处理更复杂的表达式。

文章包含AI辅助创作,作者:Edit1,如若转载,请注明出处:https://docs.pingcode.com/baike/362268

(0)
Edit1Edit1
免费注册
电话联系

4008001024

微信咨询
微信咨询
返回顶部