微信小程序實現簡單計算器


背景:最近在學習小程序開發,刷到了一個教學視頻做計算器。作者強調在微信小程序里面無法執行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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM