zoukankan      html  css  js  c++  java
  • 微信小程序实现简单计算器

    背景:最近在学习小程序开发,刷到了一个教学视频做计算器。作者强调在微信小程序里面无法执行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

    我的源码请看:https://github.com/bill1411/calc

  • 相关阅读:
    EasyMvc入门教程-高级控件说明(17)对话框控件
    EasyMvc入门教程-高级控件说明(16)信息框控件
    EasyMvc入门教程-高级控件说明(15)方位布局控件
    EasyMvc入门教程-高级控件说明(14)列布局控件
    Java5,6,7新特性
    C#文件压缩类
    FTP文件上传下载(C#)
    linux常见的系统故障排除
    sudo命令
    日志系统
  • 原文地址:https://www.cnblogs.com/yhnet/p/12331673.html
Copyright © 2011-2022 走看看