Java簡單公式計算器

  最近給公司開發業務代碼時,碰到一個場景,簡單描述是這樣的:前端

  客戶要向我們公司定製一件產品,這個產品呢,有不少屬性,那公司得根據這些屬性報價呀,怎麼報價呢?公司針對某種類型的產品有一個基準價,在同類產品下,某個屬性超標了,須要加價,但每個屬性的加價方式都不同,針對每一家客戶加多少價也不同,每一個時間點加價比率也可能不同,真實狀況要比這個複雜很多,這裏就再也不深刻討論。java

  那麼應對這種需求,我首先想到的關鍵點是:要把加價這個公式,暴露給實際能控制它的人員去輸入,把公式中須要用到的一些參數,以替代符(或者說變量)的方式提供給他們,好比,用a表示基準價,b表示屬性超出數值,而後超出部分須要乘以該屬性的單價5塊錢,那麼最終的值就能夠寫成公式: a + b * 5 ;實際運算的時候,假設a是100,b是20,把他們代替 a和b,公式就成了 100 + 20 * 5,看起來很簡單的公式,口算都能算出來,可是正常來說,公式錄入系統,是以字符串的形式保存的,一直到你把真實的值替換到公式裏,也是字符串操做,計算機要如何把你這字符串的裏的內容正確的計算出來呢?sql

  OK,其實對代碼邏輯不是很是好的同窗,能夠用一些簡單的方法,好比,將最終的公式用JavaScript的eval()函數執行一下,就能夠獲得結果了,這個方法也能夠用來在前端驗證公式錄入正確與否,還有一種方法,把最終的公式直接拼接到SQL語句,對上臨時表查一下,如:SELECT  100+20*5  AS  result from dual;也能夠獲得結果,用這兩種方法,其實還可能進行更復雜的計算,充分利用JavaScript和sql提供的函數庫。 數據庫

  嗯,回到原點,咱們如今呢,要在Java服務端實現字符串的公式正確計算,公式雖然簡單,但可能每一次要進行幾百條公式的計算,也不必查詢幾百次數據庫,並且這種和金錢相關聯的值,如非必要,仍是不要拋給前端來替你計算。雖然,公司這邊並無採用個人方案,可是我我的仍是把一個簡單的公式計算器寫了出來,留個思路,以做備用。ide

 

  簡單公式計算器可以知足 加減乘除 和 小括號的運算。函數

  我我的很是建議新手練習一下,基礎運用得越紮實,對之後的技術瓶頸突破越好。this

 

  代碼圖上的註釋比較少:spa

  粘貼出代碼圖:code

代碼來了:blog

package com.supalle.test;

import java.math.BigDecimal;
import java.math.MathContext;

/**
 * @做者: Supalle
 * @時間: 2019/3/8
 * @描述: 簡單公式計算器
 */
public class Calc {


    private char[] val;

    private int len;

    private int inx;


    // 構造器,把公式傳進去,好比: 100 + 20 * 5 + (1 + 2)
    public Calc(String val) {
        this.val = val.toCharArray();
        len = this.val.length;
        inx = 0;
    }

    // 獲取計算結果,使用方法其實就是 new Calc("100 + 20 * 5 + (1 + 2)").getResult();就能夠獲得結果了
    public BigDecimal getResult() {
        return nextValue(BigDecimal.ZERO, '+');
    }

   // OK,接下來的兩個方法,必需要弄明白,下一個值和下一個參數的區別
   // 爲何要獲取下一個值,加法、減法、和左小闊號,都須要獲取下一個值,由於加法、減法若是碰到乘法、除法,那麼運算優先權在右側,若是碰到左側小括號,優先權也在右側,因此要先把右邊的值算出來
   // 爲何要獲取下一個參數,乘法、除法,他們下一個運算符若是不是左側小括號,那麼應該從左往右順序計算,所以須要直接取到下一個參數進行計算
   // 還有一點要值得注意,那就是:在運算時,減法一概替換成加上一個負數,以此來消除實際對一個負數進行運算產生異常,好比 1 * -3,總不能檢測到 - 的時候,又去作減法運算吧
   // 就講這麼多了,不能理解的同窗,再反覆推敲幾遍
// 獲取下一個值,傳入第一個參數和第一個參數後的運算符 private BigDecimal nextValue(BigDecimal param1, char operator) { if (inx < len) { if (operator == ')') { return param1; } if (operator == '+') { return param1.add(nextValue(nextParam(), inx < len ? val[inx++] : ')')); } else if (operator == '*') { return nextValue(param1.multiply(nextParam(), MathContext.DECIMAL128), inx < len ? val[inx++] : ')'); } else if (operator == '/') { return nextValue(param1.divide(nextParam(), MathContext.DECIMAL128), inx < len ? val[inx++] : ')'); } } return param1; } // 獲取下一個參數 private BigDecimal nextParam() { char[] param = new char[len - inx + 1]; int paramInx = 0; while (inx < len) { if (val[inx] == '-') { if (paramInx == 0) { param[paramInx++] = val[inx]; param[paramInx++] = '0'; } else { val[--inx] = '+'; break; } } else if (val[inx] == '.' || ((int) val[inx] >= 48 && (int) val[inx] <= 57)) {// 若是是 . 或 0 ~ 9 param[paramInx++] = val[inx]; } else if (val[inx] == '(') { inx++; return nextValue(BigDecimal.ZERO, '+'); } else if (((int) val[inx] >= 41 && (int) val[inx] <= 43) || (int) val[inx] == 47) { break; } inx++; } return paramInx > 0 ? new BigDecimal(param, 0, paramInx) : BigDecimal.ZERO; } }
相關文章
相關標籤/搜索