Amamia17 2023-12-03 21:04 采纳率: 0%
浏览 30
已结题

复杂表达式求值程序里的函数优先级问题

【具体要求】
用户从键盘输入一个表达式,表达式中可以包含以下要素:
(1) 数值:包括整数和实数,数值可带正、负号。
(2) 一般运算符:正号、负号、加、减、乘、除、求余和乘方,其中可以包括括号。
(3) 单词(即运算函数):abs、sqrt、exp、ln、log10、sin、cos和tanh。
程序首先判断该表达式语法是否正确,如果不正确则结束程序运行;否则计算出该表达式的值,并将结果显示在屏幕上。

求解答。这是个复杂表达式求值的程序,输入中缀表达式,程序会转换成后缀表达式并求值,代码大致写出来了,注释也有。简单的运算可以执行,但是复杂一点就不行了。
下面两张是运行结果,后缀表达式转换错了,但是不知道怎么改。

img

img

   import java.util.Scanner;
import java.util.Stack;
import java.lang.Math;
public class ExpressionEvaluator {
    private static final String OPERATORS = "+-*/%^";
    private static final int[] PRECEDENCE = {1, 1, 2, 2, 3, 4};

    // 定义运算函数的名称
    private static final String FUNCTIONS = "abs sqrt exp ln log10 sin cos tanh";

    // 判断一个字符是否是运算符
    private static boolean isOperator(char ch) {
        return OPERATORS.indexOf(ch) != -1;
    }

    // 判断一个字符串是否是运算函数
    private static boolean isFunction(String str) {
        return FUNCTIONS.indexOf(str) != -1;
    }

    // 比较两个运算符的优先级
    private static boolean hasHigherPrecedence(char op1, char op2) {
        return PRECEDENCE[OPERATORS.indexOf(op1)] > PRECEDENCE[OPERATORS.indexOf(op2)];
    }

    // 对两个数值进行运算
    private static double applyOperator(char op, double num1, double num2) {
        switch (op) {
            case '+':
                return num1 + num2;
            case '-':
                return num1 - num2;
            case '*':
                return num1 * num2;
            case '/':
                if (num2 == 0) {
                    throw new ArithmeticException("除数不能为零");
                }
                return num1 / num2;
            case '%':
                return num1 % num2;
            case '^':
                return Math.pow(num1, num2);
            default:
                throw new IllegalArgumentException("无效的运算符: " + op);
        }
    }

    // 对一个数值进行运算函数
    private static double applyFunction(String func, double num) {
        switch (func) {
            case "abs":
                return Math.abs(num);
            case "sqrt":
                return Math.sqrt(num);
            case "exp":
                return Math.exp(num);
            case "ln":
                return Math.log(num);
            case "log10":
                return Math.log10(num);
            case "sin":
                return Math.sin(num);
            case "cos":
                return Math.cos(num);
            case "tanh":
                return Math.tanh(num);
            default:
                throw new IllegalArgumentException("无效的运算函数: " + func);
        }
    }

    // 将中缀表达式转换为后缀表达式
    private static String convertToPostfix(String infix) {
        StringBuilder postfix = new StringBuilder();
        Stack<Character> operatorStack = new Stack<>();

        for (int i = 0; i < infix.length(); i++) {
            char ch = infix.charAt(i);

// 跳过空格
            if (ch == ' ') {
                continue;
            }

// 如果是数字或小数点,直接追加到后缀表达式
            if (Character.isDigit(ch) || ch == '.') {
                postfix.append(ch);
            }

// 如果是字母,可能是运算函数,需要读取整个单词,并追加到后缀表达式
            else if (Character.isLetter(ch)) {
                StringBuilder word = new StringBuilder();
                while (i < infix.length() && Character.isLetter(infix.charAt(i))) {
                    word.append(infix.charAt(i));
                    i++;
                }
                i--; // 回退一位,因为循环结束时多读了一个字符

// 如果是运算函数,追加到后缀表达式,并在后面加一个空格
                if (isFunction(word.toString())) {
                    postfix.append(word).append(' ');
                } else {
                    throw new IllegalArgumentException("无效的运算函数: " + word);
                }
            }



// 如果是左括号,压入栈中
            else if (ch == '(') {
                operatorStack.push(ch);
            }

// 如果是右括号,弹出栈中的运算符,直到遇到左括号,并追加到后缀表达式
            else if (ch == ')') {
                while (!operatorStack.isEmpty() && operatorStack.peek() != '(') {
                    postfix.append(' ').append(operatorStack.pop());
                }
                if (operatorStack.isEmpty()) {
                    throw new IllegalArgumentException("不匹配的括号");
                }
                operatorStack.pop(); // 弹出左括号

// 新增的语句: 在每个括号内的表达式后面加上一个空的括号,以区分运算符
                postfix.append(" ( )");
            }
// 如果是运算符,需要考虑优先级
            else if (isOperator(ch)) {
// 在追加运算符之前,先追加一个空格,
                postfix.append(' ');
// 弹出栈中所有优先级高于或等于当前运算符的运算符,追加到后缀表达式
                while (!operatorStack.isEmpty() && operatorStack.peek() != '(' && hasHigherPrecedence(operatorStack.peek(), ch)) {
                    postfix.append(' ').append(operatorStack.pop());
                }

// 新增的判断条件: 如果栈顶是运算函数,也弹出,追加到后缀表达式
                while (!operatorStack.isEmpty() && isFunction(Character.toString(operatorStack.peek()))) {
                    postfix.append(' ').append(operatorStack.pop());
                }

// 将当前运算符压入栈中
                operatorStack.push(ch);
            }

// 如果是其他字符,抛出异常
            else {
                throw new IllegalArgumentException("无效的字符: " + ch);
            }
        }

// 弹出栈中剩余的运算符,追加到后缀表达式
        while (!operatorStack.isEmpty()) {
            char op = operatorStack.pop();
            if (op == '(') {
                throw new IllegalArgumentException("不匹配的括号");
            }
            postfix.append(' ').append(op);
        }

// 返回后缀表达式
        return postfix.toString();
    }

    // 计算后缀表达式的值
    private static double evaluatePostfix(String postfix) {
        Stack<Double> operandStack = new Stack<>();

        for (String token : postfix.split("\\s+")) {
// 如果是空字符串,跳过
            if (token.isEmpty()) {
                continue;
            }

// 如果是数值,压入栈中
            if (Character.isDigit(token.charAt(0))) {
                operandStack.push(Double.parseDouble(token));
            }

// 如果是运算函数,弹出一个数值,进行运算,然后将结果压入栈中
            else if (isFunction(token)) {
                if (operandStack.isEmpty()) {
                    throw new IllegalArgumentException("缺少运算数");
                }
                double num = operandStack.pop();
                operandStack.push(applyFunction(token, num));
            }

// 如果是运算符,弹出两个数值,进行运算,然后将结果压入栈中
            else if (isOperator(token.charAt(0))) {
                if (operandStack.size() < 2) {
                    throw new IllegalArgumentException("缺少运算数");
                }
                double num2 = operandStack.pop();
                double num1 = operandStack.pop();
                operandStack.push(applyOperator(token.charAt(0), num1, num2));
            }

// 如果是其他字符串,抛出异常
            else {
                throw new IllegalArgumentException("无效的字符串: " + token);
            }
        }

// 如果栈中只剩一个数值,那就是最终结果
        if (operandStack.size() == 1) {
            return operandStack.pop();
        }

// 如果栈中还有多个数值,说明表达式有误
        else {
            throw new IllegalArgumentException("多余的运算数");
        }
    }

    // 主函数,从键盘输入一个表达式,计算并显示结果
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入一个表达式:");
        String expression = scanner.nextLine();
        scanner.close();
        try {
            String postfix = convertToPostfix(expression);
            System.out.println("后缀表达式为: " + postfix);
            double result = evaluatePostfix(postfix);
            System.out.println("运算结果为: " + result);
        } catch (Exception e) {
            System.out.println("表达式有误: " + e.getMessage());
        }
    }}
  • 写回答

15条回答 默认 最新

报告相同问题?

问题事件

  • 系统已结题 12月11日
  • 修改了问题 12月5日
  • 创建了问题 12月3日

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵