1. 程式人生 > 其它 >用排程場演算法計算表示式的值,javascript實現

用排程場演算法計算表示式的值,javascript實現

技術標籤:javascriptjavascript演算法

最近想試下做微信小程式,剛開始寫,想找點東西練手,試下寫個計算器的小程式,查了一下,做表示式求值是用的Dijkstra的排程場演算法,用兩個棧就可以實現,一個輸出棧,一個操作符棧,演算法描述如下:

  • 依次按順序讀入,
  • 讀到數字:直接輸出;
  • 讀到一般運算子:如果棧頂的運算子優先順序不低於該運算子,則輸出棧頂運算子並使之出棧,直到棧空或不滿足上述條件為止;然後入棧;
  • 讀到左括號:直接入棧;
  • 讀到右括號:輸出棧頂運算子並使之出棧,直到棧頂為左括號為止;令左括號出棧。
  • 當讀入完畢時,依次輸出並彈出棧頂運算子,直到棧被清空。
    這裡我寫的比較簡單,詳細大家可以看以下這篇知乎上的文章:
    https://zhuanlan.zhihu.com/p/147623236
    以下是我用javascript實現的程式碼,加了一點自己的東西,也就是字串轉換成分解好的陣列的程式碼,在node.js上執行通過。
function cal(strArr){
  let first={'+':1,'-':1,'*':2,'/':2,'sqrt':3,'sin':3,'cos':3}
  let opStack=[];
  let outputStack=[];
  let calStack=[];
  for(let i=0;i<strArr.length;i++){
    let s=strArr[
i]; if(!isNaN(s)){ outputStack.push(s); }else{ if(s == '(')opStack.push(s); else if (s == ')'){ while((topOp=opStack.pop()) != '('){ outputStack.push(topOp); } }else{ if(opStack.length == 0){ opStack.
push(s) }else{ let topOp=opStack[opStack.length-1]; while(first[topOp] >= first[s]){ outputStack.push(opStack.pop()); if(opStack.length == 0)break; topOp=opStack[opStack.length-1]; } opStack.push(s) } } } } while(opStack.length > 0)outputStack.push(opStack.pop()) for(let i=0;i<outputStack.length;i++){ let s=outputStack[i]; if(!isNaN(s))calStack.push(s); else { let r=Number(calStack.pop()); let v=0; if(s== '+')v=Number(calStack.pop())+r; else if(s=='-')v=Number(calStack.pop())-r; else if(s=='*')v=Number(calStack.pop())*r; else if(s== '/')v=Number(calStack.pop())/r; else if(s == 'sqrt')v=Math.sqrt(r) else if(s == 'sin')v=Math.sin(r) else if(s == 'cos')v=Math.cos(r) calStack.push(v); } } return calStack.pop(); } //計算字串按數字和操作符分解轉換成陣列 function str2arr(str){ let resultArr=[]; const symbolSet=['+','-','*','/','(',')','sqrt','sin','cos','log'] let currentStr='' for (let s of str){ if(symbolSet.indexOf(s) > -1){ if(currentStr != '')resultArr.push(currentStr); resultArr.push(s) currentStr=''; }else{ if(s == '.'){ currentStr=currentStr+s }else{ let currentStrType= isNaN(currentStr) let sType=isNaN(s) if(sType == currentStrType){ currentStr=currentStr+s }else{ if(currentStr != '')resultArr.push(currentStr) currentStr=s } } } } if(currentStr != '')resultArr.push(currentStr) //檢查括號,左括號進棧,右括號出棧,最後棧必須為空 //檢查數字 let bracketsStack=[] for(let i=0;i<resultArr.length;i++){ if(symbolSet.indexOf(resultArr[i]) > -1){ if(resultArr[i] == '(')bracketsStack.push(resultArr[i]) else if(resultArr[i] == ')'){ if(bracketsStack.length == 0)return {result:false,msg:'括號錯誤',data:resultArr}; bracketsStack.pop() } }else if (isNaN(resultArr[i])) return {result:false,msg:'字串錯誤',data:resultArr}; } if(bracketsStack.length > 0) return {result:false,msg:'括號錯誤',data:resultArr}; return {result:true,msg:'轉換成功',data:resultArr}; } let str="1-sin(2)*3.14/5" let {result,msg,data}=str2arr(str) if(result == true){ console.log(data) let calResult=cal(data) console.log(calResult) }else console.log(msg) console.log(1-Math.sin(2)*3.14/5)

以上程式碼我親測可用,有寫的不好的地方,歡迎大家點評。