这个字符串”2*(1+3-4)“的结果是多少

2,124 阅读3分钟

大家好,我是火焱

前两天,在抖音上刷到一个计算器魔术,很有意思。

于是拿出手机尝试,发现不太对,为什么我的计算器直接把输入的内容都展示出来了?看评论区发现很多人都有类似的问题。

既然自带的计算器不好使,那就用小程序写一个。

产品描述

计算器的显示区只展示当前的数字,如果按了运算符(+ - * /),再输入数字时,展示当前的新数字,不展示之前输入的内容,按等于(=)号后,展示计算结果。

从程序员视角看,按等于(=) 时,我们拿到的是四则运算的字符串,比如:"1 + 2 * 3 - 4",然后通过代码计算这个字符串的结果,那么如何计算呢?

初步尝试

对于 javascript,很容易想到通过 eval 或者 new Function 实现,可是小程序...

image.png

既然捷径走不通,那就用逆波兰表达式来解决,我们来看下表达式的三种表示方法。

三种表示

中缀表达式,就是我们常用的表示方式:1 + 2 * 3 - 4

前缀表达式,也叫波兰表达式,是把操作符放到操作数前边,表示成:- + 1 * 2 3 4,由于后缀表达式操作起来比较方便,我们重点看下后缀表达式;

后缀表达式,也叫逆波兰表达式,它是把操作符放到操作数后边,表示成:1 2 3 * + 4 -,有了后缀表达式,我们就可以很容易计算结果了,那如何将中缀表达式转化成后序表达式呢?语言表述比较乏力,直接看代码吧,逻辑比较清晰:

/** 中缀表达式 转 后缀表达式 */
function infixToPostfix(infixExpression) {
    let output = [];
    // 存放运算符
    let stack = [];

    for (let i = 0; i < infixExpression.length; i++) {
        let char = infixExpression[i];

        if (!isOperator(char)) { // char 是数字
            output.push(char);
        } else { // char 是运算符
            while (
                // 栈不为空
                stack.length > 0 &&
                // 栈顶操作符的优先级不小于 char 的优先级
                getPrecedence(stack[stack.length - 1]) >= getPrecedence(char)
            ) {
                output.push(stack.pop());
            }
            stack.push(char);
        }
    }

    // 将剩余的运算符弹出并追加到 output 后边
    while (stack.length > 0) {
        output.push(stack.pop());
    }

    return output.join('');
}

结合下图理解一下:

表达式1 + 2 * 3 - 4

image.png

处理括号

带括号的表达式,处理逻辑和不带括号是一样的,只是多了对括号的处理。当遇到右括号时,需要把栈中左括号后面的所有运算符弹出,并追加到 output,举个例子:

计算:2 * ( 1 + 3 - 4)

image.png

通过这个例子,我们可以看出,后缀表示法居然不需要括号,更简洁。

好了,现在已经有了后序表达式,我们如何的到计算结果呢?

计算结果

计算这一步其实比较简单,直接上代码吧:

const operators = {
    '+': function (a, b) { return a + b; },
    '-': function (a, b) { return a - b; },
    '*': function (a, b) { return a * b; },
    '/': function (a, b) { return a / b; }
};

const stack = [];
postfixTokens.forEach(function (token) {
    if (!isNaN(token)) {
        stack.push(token);
    } else if (isOperator(token)) {
        var b = stack.pop();
        var a = stack.pop();
        stack.push(operators[token](a, b));
    }
});

总结

中缀表达式对于人比较友好,而后缀表达式对计算机友好,通过对数字和运算符的编排即可实现带优先级的运算。如果本文对你有帮助,欢迎点赞、评论。

参考代码:github.com/laohuoyan/m…

来试用一下成品吧(微信扫一扫下面的二维码):