背景:最近在學習小程序開發,刷到了一個教學視頻做計算器。作者強調在微信小程序里面無法執行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集合中
以下為參考文章中的原文描述:
-
自左向右讀入中綴表達式
-
數字時,加入后綴表達式;
-
運算符:
- 若為 ‘(’,入棧
- 若為 ‘)’,則依次把棧中的的運算符加入后綴表達式中,直到出現’(’,從棧中刪除’(’
- 若為除括號外的其他運算符, 當其優先級高於除’('以外的棧頂運算符時,直接入棧。否則從棧頂開始,依次彈出比當前處理的運算符優先級高和優先級相等的運算符,直到一個比它優先級低的或者遇到了一個左括號為止,然后將其自身壓入棧中(先出后入)。
-
-
當掃描的中綴表達式結束時,棧中的的所有運算符出棧;
4、計算表達式
建立一個棧W 。從左到右讀表達式,如果讀到操作數就將它壓入棧W中,如果讀到n元運算符(即需要參數個數為n的運算符)則取出由棧頂向下的n項按操作數運算,再將運算的結果代替原棧頂的n項,壓入棧W中 。如果后綴表達式未讀完,則重復上面過程,最后輸出棧頂的數值則為結束。
app.json的配置代碼

calc2:{ str:'', //臨時字符串 strList:[], //中綴表達式存儲(隊列先進先出) strListP:[], //后綴表達式(隊列先進先出) list:[], //存放運算符的堆棧 (先進后出) count:[], //計算表達式堆棧(先進后出) flag:0 //表示字符串最后一位是否是運算符號位 }
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>
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; }
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 }); } })