简易的计算器(微信小程序)

1,486 阅读3分钟

原文地址:简易的计算器(微信小程序)

开源地址:仿 ios 简单计算器的小程序(不包含科学部分)。以后还会有很多类似的。

目前在学习微信小程序开发,于是我就利用学习的栈做了一个简易的计算器。

目前还是用最简单粗暴的方式进行编写的,我会优化的,没有进行开发前的思考,一拍脑袋就做了。目前支持的运算有 + - * / % 四种,也是跟苹果手机上的计算器差不多,只不过没有科学计算的那部分。

我这里主要讲一下栈的部分,布局部分跟前端一样,觉得没什么说的,当然感兴趣的可以自己去 GitHub 上查看。

我使用的是两个栈,一个是负责存放数字,一个是负责存放运算符。

先看一下界面: 看一下结构:

// 存放运算符
operator = [];
// 存放数字
digit = [];

当拿到用户输入的字符串的时候,我会对字符串进行预处理,根据上面的图片我们可以知道像除号并不能直接使用,需要转成 / 才可以,所以我做了预处理:

// 对传入进来的字符串进行预处理,也就是消除字符串中的非运算符
// 因为有一些字符只是用于显示的,并不是计算使用的,假如
// 增加了开根号的运算,这个时候就需要将显示的根号换成方便处理的根号
// 当然我们这里没有根号,主要处理的就是运算符
pretreatmentStr(str = "") {
    let tempStr = ""
    for (let i = 0; i < str.length; ++ i) {
        let ch = str.charAt(i)
        if (ch === '−') {
            tempStr += '-'
        } else if (ch === '×') {
            tempStr += '*'
        } else if (ch === '÷') {
            tempStr += '/'
        } else {
            tempStr += ch
        }
    }
    return tempStr
}

处理完成的字符串就可以使用了,接下来我对字符串进行切割,切割成一个又一个的有效字符串,比如 32*1.2 ,变成 ["32","*","1.2"] 这样的格式,因为这样的才能方便下一步处理,当然了也可以都放到一个函数中处理,只不过我是分开的:

// 阅读字符串进而进行分类存放
readStr(str = "") {
    let formatStr = [];
    let i = 0;
    while(i < str.length) {
        switch (str.charAt(i)) {
            case '0':case '1':
            case '2':case '3':
            case '4':case '5':
            case '6':case '7':
            case '8':case '9':
            case '.':
                let digit = this.readDigit(str, i)
                i = i + digit.length
                formatStr.push(digit)
                break
            case '+':case '-':
                let sign = this.readAddOrSub(str, i)
                i += sign.length
                formatStr.push(sign)
                break
            case '*':case '/':case '%':
                formatStr.push(str.charAt(i))
            default:
                ++i
        }
    }
    return formatStr;
}

如果是数字或者 . ,那么就看看后面的字符串,以便读取完成,然后保存到 formatStr 数组中,我们看一下 readDigit 函数:

// 处理优先级为 1 的情况
readDigit(str = "", startIndex = 0) {
    let ch = str.charAt(startIndex)
    if (!this.isDigit(ch) && ch != '.') {
        return ""
    }
    return ch + this.readDigit(str, startIndex + 1)
}

很简单,我使用递归实现的,当然也可以不适用递归,递归的终止条件是不是数字或者 . 了,如果是数字或者 . 什么的,那么就直接添加到之前的数字串中。我这里没有处理像 5. 这样的字符串,因为这样的串,我不用管,只要能被 parseFloat 这个格式转换函数正确处理就没问题。

当遇到的运算符是 +- 的时候,有两种情况,第一是运算符,第二是正负号,所以我的 readAddOrSub 函数就是处理这两种情况的:

// 读取正负号,加减运算符
readAddOrSub(str = "", startIndex = 0) {
    let currentCh = str.charAt(startIndex)
    // 判断前一个是不是数字,不是数字的话就代表是符号位,而不是运算符
    if (!this.isDigit(str.charAt(startIndex - 1))) {
        // 如果是非数字,那么需要加上刚才的符号位
        return currentCh + this.readDigit(str, startIndex + 1)
    }
    return currentCh
}

我们发现对于简单的计算器来说只要前面的符号是不是数字,那么就代表这个符号是正负号,而不是运算符,如果用户输入错误怎么办,也就是 ++2 , 那么在最后计算的时候就会报错,自然也就提示了用户错误,当然也可以在用户输入的时候就让用户知道,这样的话,需要把逻辑前置,我在这就不处理这种情况,放到外面来处理。

然后我们再看怎么根据拿到的数组把对应字符放入栈中的,怎样保存运算正确的。

startCalc(str = "") {
    // 预处理字符串并整理字符串成数组
    let formatStr = this.readStr(this.pretreatmentStr(str));
    formatStr.forEach((s) => {
        // 当遇到运算符的时候
        if (s === "+" || s === "-" || s === "*" || s === "/" || s === "%") {
            // 根据运算规则处理
            this.priority(s);
            this.operator.push(s);
        } else {
            this.digit.push(parseFloat(s));
        }
    })
    while (this.operator.length !== 0) {
        let c = this.operator.pop();
        let b = this.digit.pop();
        let a = this.digit.pop();
        this.digit.push(this.calc(a - 0, c, b - 0));
    }
    return this.digit.pop();
}

下面我们具体看一下 priority 函数:

// 需要递归看优先级,直到优先级当前低或者只剩下自己
priority(s = "") {
    if (this.operator.length !== 0) {
        // 读取栈中的符号位
        let c = this.operator.pop();
        // 得到对应的运算优先级
        let cG = this.operationGrade(c);
        let sG = this.operationGrade(s);
        // 这里主要是体现两点,第一是优先级,第二是从左往右的规则
        // 把满足规则的先进行计算
        if (cG >= sG) {
            // 由于栈的特性,所以先出来的操作数
            // 后出来的才是被操作数
            let b = this.digit.pop();
            let a = this.digit.pop();
            this.digit.push(this.calc(a - 0, c, b - 0));
            // 继续跟栈中的下一个符号进行比较
            this.priority(s);
        } else {
            this.operator.push(c);
        }
    }
}

在对栈进行操作的过程中,我们要实时根据运算规则:

  1. 由左而右计算;
  2. 先算× ÷,后算 + −(先乘除后加减)

第三条规则我们这里没有所以就不写出来了。我们看一下 startCalc 函数中的下面这段代码:

while (this.operator.length !== 0) {
    let c = this.operator.pop();
    let b = this.digit.pop();
    let a = this.digit.pop();
    this.digit.push(this.calc(a - 0, c, b - 0));
}

我们发现在这里面我们是没有处理优先级的,所以在前面我们需要处理优先级,我这里是在处理优先级的过程中顺便计算了。

其他的请转至 GitHub 看详情,希望大家看完支持一下,我发现没有支持靠自己真的很难坚持下去,自制力不行的人。

开源地址:仿 ios 简单计算器的小程序(不包含科学部分)。以后还会有很多类似的。