<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                第27章 解釋器模式 27.1 四則運算你會嗎 在銀行、證券類項目中,經常會有一些模型運算,通過對現有數據的統計、分析而預測不可知或未來可能發生的商業行為。模型運算大部分是針對海量數據的,例如建立一個模型公式,分析一個城市的消費傾向,進而影響銀行的營銷和業務擴張方向。一般的模型運算都有一個或多個運算公式,通常是加、減、乘、除四則運算,偶爾也有指數、開方等復雜運算。具體到一個金融業務中,模型公式是非常復雜的,雖然只有加、減、乘、除四則運算,但是公式有可能有十多個參數,而且上百個業務品各有不同的取參路徑,同時相關表的數據量都在百萬級。呵呵,復雜了吧,不復雜那就不叫金融業務,我們來講講運算的核心——模型公式及其如何實現。 業務需求:輸入一個模型公式(加、減運算),然后輸入模型中的參數,運算出結果。 設計要求: ● 公式可以運行時編輯,并且符合正常算術書寫方式,例如a+b-c。 ● 高擴展性,未來增加指數、開方、極限、求導等運算符號時較少改動。 ● 效率可以不用考慮,晚間批量運算。 需求不復雜,若僅僅對數字采用四則運算,每個程序員都可以寫出來。但是增加了增加模型公式就復雜了。先解釋一下為什么需要公式,而不采用直接計算的方法,例如有如下3個公式: ● 業務種類1的公式:a+b+c-d。 ● 業務種類2的公式:a+b+e-d。 ● 業務種類3的公式:a-f。 其中,a、b、c、d、e、f參數的值都可以取得,如果使用直接計算數值的方法需要為每個品種寫一個算法,目前僅僅是3個業務種類,那上百個品種呢?歇菜了吧!建立公式,然后通過公式運算才是王道。 我們以實現加、減算法(由于篇幅所限,乘、除法的運算讀者可以自行擴展)的公式為例,講解如何解析一個固定語法邏輯。由于使用語法解析的場景比較少,而且一些商業公司(如SAS、SPSS等統計分析軟件)都支持類似的規則運算,親自編寫語法解析的工作已經非常少,以下例程采用逐步分析方法,帶領大家了解這一實現過程。 想想公式中有什么?僅有兩類元素:運算元素和運算符號,運算元素就是指a、b、c等符號,需要具體賦值的對象,也叫做終結符號,為什么叫終結符號呢?因為這些元素除了需要賦值外,不需要做任何處理,所有運算元素都對應一個具體的業務參數,這是語法中最小的單元邏輯,不可再拆分;運算符號就是加減符號,需要我們編寫算法進行處理,每個運算符號都要對應處理單元,否則公式無法運行,運算符號也叫做非終結符號。兩類元素的共同點是都要被解析,不同點是所有的運算元素具有相同的功能,可以用一個類表示,而運算符號則是需要分別進行解釋,加法需要加法解析器,減法需要減法解析器。分析到這里,我們就可以先畫一個簡單的類圖,如圖27-1所示。 ![](https://box.kancloud.cn/2016-08-14_57b0036b7a39b.jpg) 圖27-1 初步分析加減法類圖 這是一個很簡單的類圖,VarExpression用來解析運算元素,各個公式能運算元素的數量是不同的,但每個運算元素都對應一個VarExpression對象。SybmolExpression負責解析符號,由兩個子類AddExpression(負責加法運算)和SubExpression(負責減法運算)來實現。解析的工作完成了,我們還需要把安排運行的先后順序(加減法不用考慮,但是乘除法呢?注意擴展性),并且還要返回結果,因此我們需要增加一個封裝類來進行封裝處理,由于我們只做運算,暫時還不與業務有關聯,定義為Calculator類。分析到這里,思路就比較清晰了,優化后加減法類圖如圖27-2所示。 Calculator的作用是封裝,根據迪米特法則,Client只與直接的朋友Calculator交流,與其他類沒關系。整個類圖的結構比較清晰,下面填充類圖中的方法,完整類圖如圖27-3所示。 類圖已經完成,下面來看代碼實現。Expression抽象類如代碼清單27-1所示。 代碼清單27-1 抽象表達式類 public?abstract?class?Expression?{ ?????//解析公式和數值,其中var中的key值是公式中的參數,value值是具體的數字 ?????public?abstract?int?interpreter(HashMap<String,Integer>?var); } ![](https://box.kancloud.cn/2016-08-14_57b0036b8f58f.jpg) 圖27-2 優化后加減法類圖 ![](https://box.kancloud.cn/2016-08-14_57b0036ba65e0.jpg) 圖27-3 完整加減法類圖 抽象類非常簡單,僅一個方法interpreter負責對傳遞進來的參數和值進行解析和匹配,其中輸入參數為HashMap類型,key值為模型中的參數,如a、b、c等,value為運算時取得的具體數字。 變量解析器如代碼清單27-2所示。 代碼清單27-2 變量解析器 public?class?VarExpression?extends?Expression?{ ?????private?String?key;????? ?????public?VarExpression(String?_key){ ?????????????this.key?=?_key; ?????} ?????//從map中取之 ?????public?int?interpreter(HashMap<String,?Integer>?var)?{ ?????????????return?var.get(this.key); ?????} } 抽象運算符號解析器如代碼清單27-3所示。 代碼清單27-3 抽象運算符號解析器 public?abstract?class?SymbolExpression?extends?Expression?{ ?????protected?Expression?left; ?????protected?Expression?right;????? ?????//所有的解析公式都應只關心自己左右兩個表達式的結果 ?????public?SymbolExpression(Expression?_left,Expression?_right){ ?????????????this.left?=?_left; ?????????????this.right?=?_right; ?????} } 這個解析過程還是比較有意思的,每個運算符號都只和自己左右兩個數字有關系,但左右兩個數字有可能也是一個解析的結果,無論何種類型,都是Expression的實現類,于是在對運算符解析的子類中增加了一個構造函數,傳遞左右兩個表達式。具體的加、減法解析器如代碼清單27-4、代碼清單27-5所示。 代碼清單27-4 加法解析器 public?class?AddExpression?extends?SymbolExpression?{ ?????public?AddExpression(Expression?_left,Expression?_right){ ?????????????super(_left,_right); ?????} ?????//把左右兩個表達式運算的結果加起來 ?????public?int?interpreter(HashMap<String,?Integer>?var)?{ ?????????????return?super.left.interpreter(var)?+?super.right.interpreter(var); ?????} } 代碼清單27-5 減法解析器 public?class?SubExpression?extends?SymbolExpression?{ ?????public?SubExpression(Expression?_left,Expression?_right){ ?????????????super(_left,_right); ?????} ?????//左右兩個表達式相減 ?????public?int?interpreter(HashMap<String,?Integer>?var)?{ ?????????????return?super.left.interpreter(var)?-?super.right.interpreter(var); ?????} } 解析器的開發工作已經完成了,但是需求還沒有完全實現。我們還需要對解析器進行封裝,封裝類Calculator如代碼清單27-6所示。 代碼清單27-6 解析器封裝類 public?class?Calculator?{ ?????//定義表達式 ?????private?Expression?expression; ?????//構造函數傳參,并解析 ?????public?Calculator(String?expStr){ ?????????????//定義一個棧,安排運算的先后順序 ?????????????Stack<Expression>?stack?=?new?Stack<Expression>(); ?????????????//表達式拆分為字符數組 ?????????????char[]?charArray?=?expStr.toCharArray(); ?????????????//運算 ?????????????Expression?left?=?null; ?????????????Expression?right?=?null; ?????????????for(int?i=0;i<charArray.length;i++){ ?????????????????????switch(charArray[i])?????{ ?????????????????????case?'+':?//加法 ??????????????????????????//加法結果放到棧中 ??????????????????????????left?=?stack.pop(); ??????????????????????????right=new?VarExpression(String.valueOf(charArray[++i])); ??????????????????????????stack.push(new?AddExpression(left,right)); ??????????????????????????break; ?????????????????????case?'-': ??????????????????????????left?=?stack.pop(); ??????????????????????????right=new?VarExpression(String.valueOf(charArray[++i])); ??????????????????????????stack.push(new?SubExpression(left,right)); ??????????????????????????break; ?????????????????????default:??//公式中的變量??????????????? ??????????????????????????stack.push(new?VarExpression(String.valueOf(charArray[i]))); ?????????????????????} ?????????????}?????????? ????????????????//把運算結果拋出來 ????????????????this.expression?=?stack.pop(); ?????}????? ?????//開始運算 ?????public?int?run(HashMap<String,Integer>?var){ ????????????????return?this.expression.interpreter(var); ?????} } 方法比較長,我們來分析一下,Calculator構造函數接收一個表達式,然后把表達式轉化為char數組,并判斷運算符號,如果是“+”則進行加法運算,把左邊的數(left變量)和右邊的數(right變量)加起來就可以了,那左邊的數為什么是在棧中呢?例如這個公式:a+b-c,根據for循環,首先被壓入棧中的應該是有a元素生成的VarExpression對象,然后判斷到加號時,把a元素的對象VarExpression從棧中彈出,與右邊的數組b進行相加,b又是怎么得來的呢?當前的數組游標下移一個單元格即可,同時為了防止該元素再次被遍歷,則通過++i的方式跳過下一個遍歷——于是一個加法的運行結束。減法也采用相同的運行原理。 為了滿足業務要求,我們設置了一個Client類來模擬用戶情況,用戶要求可以擴展,可以修改公式,那就通過接收鍵盤事件來處理,Client類如代碼清單27-7所示。 代碼清單27-7 客戶模擬類 public?class?Client?{ ?????//運行四則運算 ?????public?static?void?main(String[]?args)?throws?IOException{ ??????????String?expStr?=?getExpStr(); ??????????//賦值 ??????????HashMap<String,Integer>?var?=?getValue(expStr); ??????????Calculator?cal?=?new?Calculator(expStr);?????????? ??????????System.out.println("運算結果為:"+expStr?+"="+cal.run(var)); ?????} ?????//獲得表達式 ?????public?static?String?getExpStr()?throws?IOException{ ??????????System.out.print("請輸入表達式:"); ??????????return?(new?BufferedReader(new?InputStreamReader(System.in))).readLine(); ?????}????? ?????//獲得值映射 ?????public?static?HashMap<String,Integer>?getValue(String?exprStr)?throws?IOException{ ??????????HashMap<String,Integer>?map?=?new?HashMap<String,Integer>(); ??????????//解析有幾個參數要傳遞 ??????????for(char?ch:exprStr.toCharArray()){ ???????????????if(ch?!=?'+'?&&?ch?!=?'-'){ ????????????????????//解決重復參數的問題 ????????????????????if(!map.containsKey(String.valueOf(ch))){ ?????????????????????????String?in?=?(new?BufferedReader(new?InputStreamReader?(System.in))).readLine(); ?????????????????????????map.put(String.valueOf(ch),Integer.valueOf(in)); ????????????????????} ???????????????} ??????????} ??????????return?map; ?????} } 其中,getExpStr是從鍵盤事件中獲得的表達式,getValue方法是從鍵盤事件中獲得表達式中的元素映射值,運行過程如下。 ● 首先,要求輸入公式。 請輸入表達式:a+b-c ● 其次,要求輸入公式中的參數。 請輸入a的值:100 請輸入b的值:20 請輸入c的值:40 ● 最后,運行出結果。 運算結果為:a+b-c=80 看,要求輸入一個公式,然后輸入參數,運行結果出來了!那我們是不是可以修改公式?當然可以,我們只要輸入公式,然后輸入相應的值就可以了,公式是在運行時定義的,而不是在運行前就制定好的,是不是類似于初中學過的“代數”這門課?先公式,然后賦值,運算出結果。 需求已經開發完畢,公式可以自由定義,只要符合規則(有變量有運算符合)就可以運算出結果;若需要擴展也非常容易,只要增加SymbolExpression的子類就可以了,這就是解釋器模式。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看