javascript
逆波兰计算器android源码简书,计算器的核心算法-JavaScript实现(逆波兰表达式)...
最終計(jì)算器的掩飾效果,歡迎大家來找BUG.
http://codepen.io/lvanboy/full/LxKVxJ/
功能:
1.按照運(yùn)算符的優(yōu)先級(jí)運(yùn)算
2.利用上次的結(jié)果繼續(xù)運(yùn)算
3.多個(gè)數(shù)字混合運(yùn)算
1、將一個(gè)中序表達(dá)式轉(zhuǎn)化成為逆波蘭表達(dá)式
首先維護(hù)的是兩個(gè)棧,我們這里暫且稱為S1和S2,S1中的結(jié)果最后存的就是逆波蘭表達(dá)式,S2中將用于暫時(shí)存放運(yùn)算符并且在最終形成逆波蘭表達(dá)式的時(shí)候,該棧是會(huì)清空的。下面我們看看怎樣具體的形成逆波蘭表達(dá)式。
在此首先定義一下運(yùn)算符的優(yōu)先級(jí)關(guān)系,從小到達(dá)排序,相同優(yōu)先級(jí)沒有用逗號(hào)隔開:(,+-,*\,負(fù)號(hào),)。
從左至右遍歷一個(gè)給定的中序表達(dá)式,也就是我們常規(guī)的數(shù)學(xué)計(jì)算的表達(dá)式。
(1)** 如果遇到的是數(shù)字,我們直接加入到棧S1中;**
(2)** 如果遇到的是左括號(hào),則直接將該左括號(hào)加入到棧S2中;**
(3)** 如果遇到的是右括號(hào),那么將棧S2中的運(yùn)算符一次出棧加入到棧S1中,直到遇到左括號(hào),但是該左括號(hào)出棧S2并不加入到棧S1中;**
(4)** 如果遇到的是運(yùn)算符,包括單目運(yùn)算符和雙目運(yùn)算符,我們按照下面的規(guī)則進(jìn)行操作:**
如果此時(shí)棧S2為空,則直接將運(yùn)算符加入到棧S2中;
如果此時(shí)棧S2不為空,當(dāng)前遍歷的運(yùn)算符的優(yōu)先級(jí)大于等于棧頂運(yùn)算符的優(yōu)先級(jí),那么直接入棧S2;
如果此時(shí)棧S2不為空,當(dāng)前遍歷的運(yùn)算符的優(yōu)先級(jí)小于棧頂運(yùn)算符的優(yōu)先級(jí),則將棧頂運(yùn)算符一直出棧加入到棧S1中,直到棧為空或者遇到一個(gè)運(yùn)算符的優(yōu)先級(jí)小于等于當(dāng)前遍歷的運(yùn)算符的優(yōu)先級(jí),此時(shí)將該運(yùn)算符加入到棧S2中;
(5)** 直到遍歷完整個(gè)中序表達(dá)式之后,棧S2中仍然存在運(yùn)算符,那么將這些運(yùn)算符依次出棧加入到棧S1中,直到棧為空。**
按照上面的五條操作反復(fù)進(jìn)行完成,那么棧S1中存放的就是逆波蘭表達(dá)式。
2、利用逆波蘭表達(dá)式求值
利用逆波蘭表達(dá)式求計(jì)算式的值其實(shí)很簡單,正式因?yàn)檫@一點(diǎn),所以逆波蘭表達(dá)式才在編譯原理中被用于計(jì)算一個(gè)表達(dá)式的值。
下面來具體看看如何求一個(gè)逆波蘭表達(dá)式的值:
我們此時(shí)維護(hù)一個(gè)數(shù)據(jù)結(jié)果棧S3,我們將會(huì)看到該棧中最后存放的是最終的表達(dá)式的值。我們從左至右的遍歷棧S1,然后按照下面的規(guī)則進(jìn)行操作棧S3.
(1)** 如果遇到的是數(shù)字,那么直接將數(shù)字壓入到S3中;**
(2) ** 如果遇到的是單目運(yùn)算符,那么取S3棧頂?shù)囊粋€(gè)元素進(jìn)行單目運(yùn)算之后,將結(jié)果再次壓入到棧S3中;**
(3)** 如果遇到的是雙目運(yùn)算符,那么取S3棧頂?shù)膬蓚€(gè)元素進(jìn)行,首先出棧的在左,后出棧的在右進(jìn)行雙目運(yùn)算符的計(jì)算,將結(jié)果再次壓入到S3中。**
按照上面的三個(gè)規(guī)則,遍歷完整個(gè)棧S1,那么最后S3中的值就是逆波蘭表達(dá)式的值了,所以我們可以看出來使用逆波蘭表達(dá)式進(jìn)行求值是很簡單的,只有兩種操作要么是直接壓棧,要么是運(yùn)算之后將結(jié)果壓棧。
3、JavaScript 具體實(shí)現(xiàn)代碼,為了方便dom操作引用了jQuery
HTML 結(jié)構(gòu)是這樣的:樣式大家自己發(fā)揮
calculatorCalculator
下面是參考代碼:這里獨(dú)立實(shí)現(xiàn)了AC,Ans,CE(在原先的算法實(shí)現(xiàn))
$(function(){
var nums = ["AC","CE","%","/",
"7","8","9","*","4","5","6","-",
"1","2","3","+",".","0","Ans","="];
//利用數(shù)組模擬堆棧,新建兩個(gè)堆棧
//s1 的結(jié)果 存放的是 逆波蘭表達(dá)式,
//s2 暫時(shí)存放運(yùn)算符
var s1 = [] ,s2 = [];
// 定義運(yùn)算符 優(yōu)先級(jí) ,
var signPriority = [["+","-"],["*","/","%"],["-","."]];
// 定義數(shù)字正則
var reg = /\d+/;
// 單目規(guī)則
var regSingle = /[\-\.]/;
// 不可顯示字符
var hidden = /[AC=CE=Ans]/;
// 連接顯示數(shù)字
var concatNum="";
// 保存結(jié)果
var s3 = [],result;
//連接計(jì)算數(shù)字
var sum ="";
//計(jì)CE的次數(shù)
var count = 0;
//迭代元素
var key = $("#nums").children();
key.each(function(index,val){
//寫入計(jì)算器按鈕的值
$(val).text(nums[index]);
//實(shí)現(xiàn)的按鈕動(dòng)態(tài)效果
$(val).on("mousedown",function(e){
$(this).css({"box-shadow":"-1px -1px 3px #666"})
})
$(val).on("mouseup",function(e){
$(this).css({"box-shadow":"2px 2px 3px #555"})
})
$(val).on("click",function(e){
//顯示字符
if(!hidden.test($(this).text())){
concatNum += $(this).text();
$("#output").text(concatNum);
}
//如果是數(shù)字,拼接數(shù)字
if(reg.test($(this).text())){
sum += $(this).text();
}else{
s1.push(parseInt(sum));
sum =" ";
//Ans 保存上一次的值,不顯示該值
if($(this).text()=="Ans"){
s1.push(s3[0]);
s3 = [];
}else{
s3 = [];
}
//AC功能清空屏幕,不顯示該鍵值
if($(this).text()=="AC"){
$("#output").text("");
concatNum="";
s1 = [];
s2 = [];
s3 = [];
}
//CE清除一個(gè)值,不顯示該值
if($(this).text()=="CE"){
concatNum = concatNum.substr(0,concatNum.length-1);
// s1.pop();
count++;
s1[s1.length-count]=parseInt(concatNum);
$("#output").text(concatNum);
}
//如果s2 為空,直接將運(yùn)算符壓入s2中
if(s2.length==0){
s2.push($(this).text());
}else{
//如果s2不為空,判斷運(yùn)算符優(yōu)先級(jí)
var sTopElem = s2[s2.length-1];
var curElem = $(this).text();
//如果當(dāng)前優(yōu)先級(jí)大于棧頂優(yōu)先級(jí),直接入s2
if(judgePriority(curElem,sTopElem)){
s2.push(curElem);
}else{
//堆棧不為空,當(dāng)前元素不小于棧頂元素優(yōu)先級(jí),一直從s2出棧到s1
while(!judgePriority(curElem,sTopElem)&&s2.length!==0){
s1.push(s2.pop());
}
//不滿足上面條件,再把當(dāng)前符號(hào)入棧s2
s2.push($(this).text());
}
}
}
//計(jì)算逆波蘭堆棧
if($(this).text()=="="){
var tem = [];
//清空鏈接字符
concatNum="";
//清空符號(hào)
s2 = [];
//清空計(jì)數(shù)器
count = 0;
//遍歷s1
for(var i=0;i
//如果當(dāng)前為數(shù)字,直接入棧s3
if(reg.test(s1[i])){
s3.push(parseFloat(s1[i]));
}
//單目運(yùn)算
else if(regSingle.test(s1[i])){
if(s1[i]=="-"){
s3.push(-s3.pop());
}
if(s1[i]=="."){
s3[s3.length-2]=(s3.pop()/10);
}
}else{ //雙目運(yùn)算
if(s1[i]=="+"){
s3.push(parseFloat(s3.pop())+parseFloat(s3.pop()));
}
if(s1[i]=="-"){
s3.push(parseFloat(s3.pop())-parseFloat(s3.pop()));
}
if(s1[i]=="*"){
s3.push(parseFloat(s3.pop())*parseFloat(s3.pop()));
}
if(s1[i]=="/"){
s3.push(parseFloat(s3.pop())/parseFloat(s3.pop()));
}
if(s1[i]=="%"){
s3.push(parseFloat(s3.pop())%parseFloat(s3.pop()));
}
}
}
//清空逆波蘭
s1=[];
//最終結(jié)果s3
if(s3.length==1){
//判斷結(jié)果是否非法
if(isNaN(s3[0])){
$("#output").text("非法操作");
s3 = [];
}else{
$("#output").text(s3[0]);
}
}else{
result=s3[0]+s3[1];
if(isNaN(result)){
$("#output").text("非法操作");
s3 = [];
}else{
$("#output").text(result);
}
}
}
})
//判斷當(dāng)前運(yùn)算符與棧頂運(yùn)算符優(yōu)先級(jí)
function judgePriority(sign1,sign2){
var index1,index2;
for(var i=signPriority.length-1;i>=0;i--){
for(var j=signPriority[i].length-1;j>=0;j--){
if(sign1==signPriority[i][j]){
index1=i;
}
if(sign2==signPriority[i][j]){
index2=i;
}
}
}
if(index1>index2){
return true;
}else{
return false;
}
};
})
})
總結(jié)
以上是生活随笔為你收集整理的逆波兰计算器android源码简书,计算器的核心算法-JavaScript实现(逆波兰表达式)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android m权限工具类,andro
- 下一篇: IOS开发之JSON序列化从客户端发送到