微信小程序实现简单计算器

背景:最近在学习小程序开发,刷到了一个教学视频做计算器。作者强调在微信小程序里面无法执行eval方法 。想用Function进行构造,还是不被执行。

          我好奇的搜了下发现很多人都碰到这个问题,就想自己实现一下,但是现实非常打脸,想了一天多时间,也没找到突破口,最后就在网上找到了

          zl_calculator_zl,借鉴了先进思想,也就有了这篇文章。

思路来源于:https://github.com/zhangluzhanglu/zl_calculator_zl

博主提供了一套支持()运算符的计算类并且在ReadMe文件中写了实现原理,本文也是用了此原理实现了加减乘除余运算。

主要思路:

1、记录用户的输入并把它显示在计算器中(其中要处理C键和退格键操作以及重复运算符输入的问题,另外要注意小数点和负数问题)

2、将记录的内容,转化为中缀表达式集合 比如:103+3.5*5-9/7 就转化为 【“103”、“+”、“3.5”、“*”、“5”、“-”、“9”、“/”、“7”】

3、再将中缀表达式集合改为后缀表达式集合  比如:【“103”、“+”、“3.5”、“*”、“5”、“-”、“9”、“/”、“7”】转化为【“103”、“3.5”、“5”、“*”,“+”,“9”、“7”,“/”、“-”】

用自己的话总结就是:

  从左往右读取中缀表达式集合(需要另外一个集合S来存储读到的运算符)

      a、碰到数字,直接加入后缀表达式集合P中

      b、碰到运算符,判断S,S为空,直接存储当前运算符,

                                              S不为空,则判断S中最近一次新加入的运算符和当前拿到的运算符优先级谁高    当前拿到的运算符等级高或优先级相等,则直接存入S集合

                                                                                                                                                                              当前拿到的运算符登记低于S最近新加入的,则先把S集合中所有的运算符依次弹出,加入到后缀表达式集合P中,再清空S集合,将拿到的运算符存在S集合中

         以下为参考文章中的原文描述:

  1. 自左向右读入中缀表达式

    • 数字时,加入后缀表达式;

    • 运算符:

      • 若为 ‘(’,入栈
      • 若为 ‘)’,则依次把栈中的的运算符加入后缀表达式中,直到出现’(’,从栈中删除’(’
      • 若为除括号外的其他运算符, 当其优先级高于除’('以外的栈顶运算符时,直接入栈。否则从栈顶开始,依次弹出比当前处理的运算符优先级高和优先级相等的运算符,直到一个比它优先级低的或者遇到了一个左括号为止,然后将其自身压入栈中(先出后入)。
  2. 当扫描的中缀表达式结束时,栈中的的所有运算符出栈;

4、计算表达式

建立一个栈W 。从左到右读表达式,如果读到操作数就将它压入栈W中,如果读到n元运算符(即需要参数个数为n的运算符)则取出由栈顶向下的n项按操作数运算,再将运算的结果代替原栈顶的n项,压入栈W中 。如果后缀表达式未读完,则重复上面过程,最后输出栈顶的数值则为结束。

app.json的配置代码

calc2:{
    str:'',   //临时字符串
    strList:[], //中缀表达式存储(队列先进先出)
    strListP:[],  //后缀表达式(队列先进先出)
    list:[],  //存放运算符的堆栈 (先进后出)
    count:[],     //计算表达式堆栈(先进后出)
    flag:0       //表示字符串最后一位是否是运算符号位
  }
View Code

wxml代码

<!--pages/calc-v2/calc-v2.wxml-->
<view class="calc">
  <view class="content">
    <text>{{express}}</text>
    <text>{{result}}</text>
  </view>

  <view class="btn">
    <view class="btn-view">
      <text bindtap="click" data-con="c" class="btn-view-text-color">c</text>
      <text bindtap="click" data-con="÷" class="btn-view-text-color">÷</text>
      <text bindtap="click" data-con="×" class="btn-view-text-color">×</text>
      <text bindtap="click" data-con="←" class="btn-view-text-color"></text>
    </view>
    <view class="btn-view">
      <text bindtap="click" data-con="7">7</text>
      <text bindtap="click" data-con="8">8</text>
      <text bindtap="click" data-con="9">9</text>
      <text bindtap="click" data-con="-" class="btn-view-text-color">-</text>
    </view>
    <view class="btn-view">
      <text bindtap="click" data-con="4">4</text>
      <text bindtap="click" data-con="5">5</text>
      <text bindtap="click" data-con="6">6</text>
      <text bindtap="click" data-con="+" class="btn-view-text-color">+</text>
    </view>
    <view class="bottom">
      <view class="left">
        <view class="btn-view-123">
          <text bindtap="click" data-con="1">1</text>
          <text bindtap="click" data-con="2">2</text>
          <text bindtap="click" data-con="3">3</text>
        </view>
        <view class="btn-view-123">
          <text bindtap="click" data-con="%">%</text>
          <text bindtap="click" data-con="0">0</text>
          <text bindtap="click" data-con=".">.</text>
        </view>
      </view>
      <view class="right">
        <view bindtap="result" data-con="="  class="denghao">
          <text>=</text>
        </view>
      </view>
    </view>
  </view>
</view>
View Code

wxss代码

/* pages/calc-v2/calc-v2.wxss */
.calc{margin: 20rpx;}
.content{
  border: 1px #b4b5b6 solid;
  width: 710rpx;height: 150rpx;
  text-align: right;
  display: flex;
  flex-direction: column;        /*元素的排列方向为垂直*/
  font-size: 20px;
  font-weight:400;}

.content text {
    height: 70rpx;
    margin-top: 10rpx;
    margin-right: 20rpx;}

.btn {
  text-align: center;
  width: 100%;
  height: 874rpx;
  font-weight:400;font-size: 20px;}

  .btn-view{
    display: flex; 
    justify-content:space-between;
    height: 175rpx;
    
  }

  .btn-view-123{
    height: 175rpx;
    text-align: left;
    display: flex; 
  }

  .btn-view-123 text{
    width: 175rpx;
    height: 175rpx;
    border: 1px #b4b5b6 solid;
    border-radius: 5rpx;
    line-height: 175rpx;
    text-align: center;
  }



  .btn-view text{
    width: 175rpx;
    height: 175rpx;
    border: 1px #b4b5b6 solid;
    border-radius: 5rpx;
    line-height: 175rpx;
    text-align: center;
  }

  .btn-view-text-color{
    color: #169fe6;
  }

  .bottom{
    display: flex;
  }

  .denghao{
    
    border: 1px #b4b5b6 solid;
    text-align: center;
    line-height: 350rpx;
    height: 350rpx;
    width: 175rpx;
    background: #169fe6;
  }

  .denghao text{
    color: white;
    height: 350rpx;
    width: 175rpx;
    line-height: 175rpx;
  }
View Code

js代码

// pages/calc/calc.js

const app = getApp()

Page({

  /**
   * 页面的初始数据
   */
  data: {
    express: '', //第一行的表达式
    result: '' //第二行的结果
  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function() {

  },

  //给所有text或view绑定此事件,同时增加对应的自定义属性值
  click(e) {
    //console.log(e.target.dataset.con)

    let input = e.target.dataset.con //获取每次输入的内容
    if (input == "c") {
      this.handleClear();
    } else if (input == "←") {
      this.handleDelete();
    } else {
      //调用处理字符串
      this.handleInfo(input);
    }


  },

  //处理本地用户的输入操作
  handleInfo(input) {
    if (app.calc2.str.length == 0) { //第一次点击
      if (input == "-" || this.checkShuZi(input)) { //为减号
        this.addStr(input);
      } else {
        return;
      }
    } else {
      if (app.calc2.flag == 1) { //说明最后一位是符号
        if (this.checkFuHao(input)) {
          app.calc2.str = app.calc2.str.substring(0, app.calc2.str.length - 1); //去掉最后一位符号,添加最新的符号进去
          this.addStr(input);
        } else {
          this.addStr(input);
        }
      } else {
        this.addStr(input);
      }
    }
  },

  //客户点击等号了
  result() {
    //每次点击等号重新把列表给空
    app.calc2.strList.length = 0;
    app.calc2.strListP.length = 0;
    app.calc2.list.length = 0;
    app.calc2.count.length = 0;

    //将表达式变成中缀表达式队列
    this.expressToStrList(this.data.express);
    console.log(app.calc2.strList);

    //将中缀表达式集合赋值给临时变量
    let tempList = app.calc2.strList;
    this.expressToStrListP(tempList);
    console.log(app.calc2.strListP);

    //最终的计算
    let tempP = app.calc2.strListP
    for (let m in tempP){
      if (this.checkFuHao2(tempP[m])) {//不含点号的符号方法判断
        let m1 = app.calc2.count[0];  //取出第一个数据
        app.calc2.count.shift();      //取出后删除该数据
        let m2 = app.calc2.count[0];  
        app.calc2.count.shift();
        // console.log('m1是' +m1);
        // console.log('m2是' + m2);
        // console.log('运算符是' + tempP[m]);
        // console.log('计算结果是:' + this.countDetail(m2, tempP[m], m1));
        app.calc2.count.unshift(this.countDetail(m2, tempP[m], m1));  //将计算结果放到count中
      }else{
        app.calc2.count.unshift(tempP[m])  //将数字压进去
      }
    }
    console.log('最终的计算结果是' + app.calc2.count[0]);
    this.setData({
      result: app.calc2.count[0]
    });
  },

  //实际具体计算
  countDetail(e1, e2, e3) {
    let result = 0.0;
    console.log(e2);
    try {
      if (e2 == "×") {
        result = parseFloat(e1) * parseFloat(e3);
      } else if (e2 == "÷") {
        result = parseFloat(e1) / parseFloat(e3);
      } else if (e2 == "%") {
        result = parseFloat(e1) % parseFloat(e3);
      } else if (e2 == "+") {
        result = parseFloat(e1) + parseFloat(e3);
      } else {
        result = parseFloat(e1) - parseFloat(e3);
      }
    } catch (error) {

    }
    return result;
  },
  
  //将中缀表达式集合转变为后缀表达式集合
  expressToStrListP(tempList){
    for (let item in tempList) {
      if (this.checkFuHao2(tempList[item])) { //不含点号的符号方法判断
        if (app.calc2.list.length == 0) {
          app.calc2.list.unshift(tempList[item]); //直接添加添加运算符
        } else {
          if (this.checkFuHaoDX(app.calc2.list[0], tempList[item])) {
            for (let x in app.calc2.list) {
              app.calc2.strListP.push(app.calc2.list[x]);   //将运算符都放到listP中
            }
            app.calc2.list.length = 0; //循环完把list置空
            app.calc2.list.unshift(tempList[item]);//加新元素进去
          } else {
            app.calc2.list.unshift(tempList[item]); //直接添加添加运算符
          }
        }
      } else {
        app.calc2.strListP.push(tempList[item]); //数字直接加到后缀集合中
      }
    }
    //循环完有可能最后一个是数字了,取到的不是字符,那么运算符里剩余的还的加到listP中
    if (app.calc2.list.length > 0) {
      for (let x in app.calc2.list) {
        app.calc2.strListP.push(app.calc2.list[x]);   //将运算符都放到listP中
      }
      app.calc2.list.length = 0; //循环完把list置空
    }
  },

  //判断两个运算符的优先级(m1是list集合中最后加进去的那个元素比较将要进来的元素,如果m1比m2大,弹出list集合到listp中,再把m2加到list中,否则直接将m2加入list)
  checkFuHaoDX(m1, m2) {
    if ((m1 == "%" || m1 == "×" || m1 == "÷") && (m2 == "-" || m2 == "+")) {
      return true;
    } else {
      return false;
    }
  },

  //将字符串表达式变成中缀队列
  expressToStrList(express) {
    let temp = ''; //定义临时变量
    //将表达式改为中缀队列
    for (let i = 0; i < express.length; i++) {
      if (i == 0 && express[i] == "-") {
        temp = temp + express[i];
      } else {
        if (this.checkShuZi2(express[i])) { //如果i是数字
          temp = temp + express[i];
        } else {
          if (temp.length > 0) {
            if (express[i] == ".") {
              temp = temp + express[i];
            } else {
              app.calc2.strList.push(parseFloat(temp));
              temp = '';
              app.calc2.strList.push(express[i]);
            }
          } else {
            temp = temp + express[i];
          }
        }
      }
    }
    //循环到最后再看temp中有没有数字了,如果有加进来
    if (temp.length > 0 && this.checkShuZi(temp.substring(temp.length - 1))) {
      app.calc2.strList.push(parseFloat(temp));
      temp = '';
    }
  },

  //处理客户输入清除键
  handleClear() {
    app.calc2.str = '';
    app.calc2.strList.length = 0;
    app.calc2.strListP.length = 0;
    app.calc2.list.length = 0;
    app.calc2.count.length = 0;
    app.calc2.minusFlag = 0;
    this.setData({
      express: '',
      result: ''
    });
  },
  //处理客户输入回退键
  handleDelete() {
    let str = app.calc2.str;
    if (str.length > 0) {
      str = str.substring(0, str.length - 1);
      app.calc2.str = str;
      this.setData({
        express: str,
      });
    } else {
      return;
    }
  },

  //判断是否是运算符(含点号,用在组织表达式时 .不能重复输入)
  checkFuHao(input) {
    if (input == "-" || input == "+" || input == "÷" || input == "%" || input == "×" || input == ".") {
      return true;
    } else {
      return false;
    }
  },

  //判断是否是运算符(不含点号)
  checkFuHao2(input) {
    if (input == "-" || input == "+" || input == "÷" || input == "%" || input == "×") {
      return true;
    } else {
      return false;
    }
  },

  //判断是否是数字
  checkShuZi(input) {
    if (input == "0" || input == "1" || input == "2" ||
      input == "3" || input == "4" || input == "5" ||
      input == "6" || input == "7" || input == "8" || input == "9") {
      return true;
    } else {
      return false;
    }
  },

  //判断是否是数字(包含.号,用在表达式转中缀方法中)
  checkShuZi2(input) {
    if (input == "0" || input == "1" || input == "2" ||
      input == "3" || input == "4" || input == "5" ||
      input == "6" || input == "7" || input == "8" || input == "9" || input == ".") {
      return true;
    } else {
      return false;
    }
  },

  //给字符串添加新字符(直接追加 在判断是否要改变变量flag)
  addStr(input) {
    app.calc2.str = app.calc2.str + input;
    if (this.checkFuHao(input)) {
      app.calc2.flag = 1; //设置标记位位1
    } else {
      app.calc2.flag = 0;
    }
    this.setData({
      express: app.calc2.str
    });
  }

})
View Code

猜你喜欢

转载自www.cnblogs.com/yhnet/p/12331673.html