begin: 20170804
version: 20170804
該文是對(duì)fcc上計(jì)算器項(xiàng)目的總結(jié)
項(xiàng)目題目在https://freecodecamp.cn/challenges/build-a-javascript-calculator
我完成的項(xiàng)目在https://codepen.io/jacktown/pen/ZJBdPq
其github地址是https://github.com/jacktown11/fcc/tree/master/calculator苇经。
概述
- 項(xiàng)目利用css3技術(shù)實(shí)現(xiàn)了一個(gè)較有立體感的計(jì)算器界面坟冲;
- 可以實(shí)現(xiàn)簡單的加、減撮奏、乘揽惹、除、取模運(yùn)算,用戶在輸入過程中可以取消上步輸入谊却、清空所有輸入;
- 每次用戶有輸入時(shí)js都會(huì)判斷當(dāng)前輸入的算式是否合法哑芹,如果不合法讓算式顯示為紅色炎辨,以提示用戶有輸入錯(cuò)誤;
- 支持結(jié)果引用聪姿,但是在結(jié)果為大數(shù)(采用科學(xué)計(jì)數(shù)法表示)時(shí)碴萧,會(huì)有一點(diǎn)問題,但總體不影響使用末购;
- 不支持高級(jí)數(shù)學(xué)函數(shù)破喻、括號(hào),總體功能比較初級(jí)盟榴。
npm和gulp
該項(xiàng)目在整個(gè)項(xiàng)目過程中采用了npm包管理工具來下載依賴文件曹质,安裝gulp來實(shí)現(xiàn)文件的自動(dòng)化壓縮、導(dǎo)出,對(duì)我來說是一個(gè)新技能的應(yīng)用嘗試咆繁。
界面
界面的實(shí)現(xiàn)主要技術(shù)點(diǎn)是CSS3陰影讳推,實(shí)現(xiàn)了計(jì)算器邊緣立體效果、按鈕的陰影效果玩般、顯示行內(nèi)嵌效果银觅。
寫css過程中遇到的一個(gè)難點(diǎn)是:要讓輸入的算式靠在右邊且不換行,這本可以直接text-align:center;white-space:nowrap;
就OK的坏为,但是當(dāng)算式很長時(shí)究驴,文本行卻是靠著顯示框左邊框開始顯示的,右側(cè)的文本顯示到了顯示框之外匀伏,把overflow
hidden以后洒忧,結(jié)果顯示框內(nèi)顯示的就是算式開始部分而不是結(jié)尾部分。怎么解決呢够颠?將顯示的文本再包裹上一個(gè)行內(nèi)塊元素熙侍,絕對(duì)定位right:0;
,不設(shè)寬度履磨,當(dāng)然還要右對(duì)齊不換行蛉抓,這樣文本右側(cè)總是對(duì)齊在顯示框的右邊了。
在解決上述難點(diǎn)的過程中剃诅,我考慮過讓文本方向從右往左(text-direction:rtl;
)巷送,然后在js生成文本顯示是將文本本身翻轉(zhuǎn)一下,這樣就正常右對(duì)齊了矛辕,但是還是不行笑跛,因?yàn)槲谋緩挠彝箫@示是指單詞的顯示順序,然而單詞內(nèi)部的字母卻仍舊是從左往右的聊品!而瀏覽器對(duì)單詞的識(shí)別又不簡單地使用空格飞蹂,由于算式中有小數(shù)點(diǎn)、運(yùn)算符杨刨、數(shù)字晤柄,要把算式文本調(diào)整得符合瀏覽器的識(shí)別方法,其過程會(huì)十分復(fù)雜妖胀。故放棄了這種方案。
算法
該項(xiàng)目界面比較簡單惠勒,除了CSS3外赚抡,主要技術(shù)點(diǎn)都集中在算法上,主要包括四個(gè)方面:
- 根據(jù)用戶輸入識(shí)別操作數(shù)和操作符
- 根據(jù)用戶輸入進(jìn)行數(shù)據(jù)的壓入纠屋、回退涂臣、清空等操作
- 算式合法性檢驗(yàn)
- 算式計(jì)算
輸入識(shí)別與處理
這兩個(gè)比較細(xì)節(jié),難度并不大。有兩個(gè)思路:
- 程序直接保存用戶輸入的字一個(gè)長字符串赁遗,每次新輸入后都將這個(gè)長字符串重新解析出操作數(shù)和操作符數(shù)組署辉,那么每次新輸入的計(jì)算都應(yīng)該是O(n)的時(shí)間復(fù)雜度,但是這樣的好處是編程會(huì)相對(duì)清晰一點(diǎn)岩四;
- 程序保存根據(jù)之前輸入已經(jīng)處理好的操作數(shù)和操作符數(shù)組哭尝,新輸入時(shí)修改這個(gè)數(shù)組。
經(jīng)過該過程以后剖煌,用戶的輸入被處理成一個(gè)算式數(shù)組材鹦,數(shù)組元素是操作符或操作數(shù)。
算式合法性檢驗(yàn)
檢驗(yàn)依據(jù):
- 算式第一項(xiàng)必須是操作數(shù)或正負(fù)號(hào)
- 算式最后一項(xiàng)必須是操作數(shù)
- 操作數(shù)和操作符必須相間出現(xiàn)
- 單獨(dú)出現(xiàn)的小數(shù)點(diǎn)非法
另外還需要區(qū)分檢驗(yàn)的算式是要立馬用于計(jì)算(用戶點(diǎn)擊了=
號(hào))還是后續(xù)還有輸入耕姊。
算式計(jì)算
其中算式計(jì)算相對(duì)復(fù)雜桶唐,有一個(gè)極簡的思路是利用eval()全局函數(shù),直接將用戶輸入的字符串傳入計(jì)算茉兰,得到結(jié)果尤泽,主要問題在于:無法解決浮點(diǎn)數(shù)計(jì)算誤差問題。
為了程序的完善及出于練習(xí)編程的考慮规脸,我選擇了自己根據(jù)算式字符串編寫算法坯约,算法的主要思路如下:
- 首先算式必須通過合法性檢驗(yàn),之后進(jìn)行計(jì)算燃辖;
- 計(jì)算分兩步進(jìn)行:先計(jì)算乘鬼店、除、取模高優(yōu)先級(jí)運(yùn)算黔龟,再進(jìn)行加減運(yùn)算妇智;
- 首先進(jìn)行乘、除氏身、取模運(yùn)算巍棱,我們稱它們?yōu)橐患?jí)操作符,在算式數(shù)組中從左往右找到第一個(gè)一級(jí)操作符蛋欣,將其左右兩個(gè)操作數(shù)用該操作符運(yùn)算得到一個(gè)結(jié)果航徙,將該操作符及其左右兩個(gè)操作數(shù)從算式數(shù)組中剔除,將計(jì)算得到的結(jié)果插入到它們的位置上(利用Array的splice方法可以輕松實(shí)現(xiàn))陷虎。重復(fù)這一查找到踏、計(jì)算、剔除尚猿、插入的過程窝稿,直到算式數(shù)組中不存在一級(jí)運(yùn)算。此后按照同樣的方法進(jìn)行二級(jí)運(yùn)算凿掂。二級(jí)運(yùn)算完畢后伴榔,算式數(shù)組長度為1,其內(nèi)容就是計(jì)算結(jié)果。
上面提到了:計(jì)算中會(huì)出現(xiàn)浮點(diǎn)數(shù)計(jì)算不準(zhǔn)確的問題踪少,比如0.01+0.2
的準(zhǔn)確結(jié)果是0.3
塘安,但是程序計(jì)算結(jié)果可能類似0.210000000000004
,這是由于計(jì)算機(jī)用二進(jìn)制保存浮點(diǎn)數(shù)的精度限制所致援奢。怎樣解決呢兼犯?我們可以將所有浮點(diǎn)運(yùn)算轉(zhuǎn)換為整數(shù)運(yùn)算,如這個(gè)式子我們可以計(jì)算1+20
的結(jié)果然后除以100萝究,就可以得到準(zhǔn)確結(jié)果了免都。這個(gè)處理需要貫穿到上述所說的算法的實(shí)現(xiàn)中。