當前本身開發的 Android 項目是一個智能推薦系統,用到 drools 規則引擎,於我來講是一個新知識點,之前都沒據說過的東東,不過用起來也不算太難,通過一段時間學習,基本掌握。關於 drools 規則引擎的內容,後面再整理JBoss 官網上面有詳細的文檔,網上資料也比較多。學習 drools 規則引擎的傳送門:html
Drools 官網首頁: https://www.drools.org/
Drools 官方文檔: https://docs.jboss.org/drools/release/7.12.0.Final/drools-docs/html_single/index.htmljava
這裏主要是由於本身使用 Android Studio 在編寫 drools 文件時,沒有了智能提示,IDE 不對語法進行檢查了,出現了兩次多寫 )
的錯誤。這就跟用記事本寫東西程序同樣,慌的不行,因此本身寫一個簡單的語法檢查的腳本。對 drools 文件進行一個初步的判斷。python
對於某些企業級應用,常常會有大量的、錯綜複雜的業務規則配置,用程序語言來描述,就形如:if-else
或者 switch-case
等。像這種不一樣的條件,作不一樣的處理就是一種規則。用通俗易懂的結構來表示:當 XXX 的時候,作 XXX 的事。理論上這樣的問題均可以用規則引擎來解決。可是咱們也不是說爲了使用規則引擎去使用它,咱們視具體業務邏輯而定,通常來講,條件(規則)比較複雜,狀況種類比較多,條件可能會常常變化等,這時候,選擇規則引擎去解決問題是比較明智的。
譬如隨着企業管理者的決策變化,某些業務規則也會隨之發生更改。對於咱們開發人員來講,咱們不得不一直處理軟件中的各類複雜問題,須要將全部數據進行關聯,還要儘量快地一次性處理更多的數據,甚至還須要以快速的方式更新相關機制。正則表達式
Drools 規則引擎實現了將業務決策從應用程序中分離出來。
優勢:
一、簡化系統架構,優化應用
二、方便系統的整合,它們是獨立的,容許不一樣背景的人進行合做
三、減小編寫「硬代碼」業務規則的成本和風險,每一個規則控制所需的最小信息量
四、它們很容易更新,提升系統的可維護性,減少維護成本數據結構
咱們須要傳遞進去數據,用於規則的檢查,調用外部接口,同時還可能獲取規則執行完畢以後獲得的結果架構
指傳遞給drools腳本的對象,是一個普通的javabean,原來javaBean對象的引用,能夠對該對象進行讀寫操做,並調用該對象的方法。當一個java bean插入到working Memory(內存存儲)中,規則使用的是原有對象的引用,規則經過對fact對象的讀寫,實現對應用數據的讀寫,對其中的屬性,須要提供get和set方法,規則中能夠動態的前往working memory中插入刪除新的fact對象app
例子:
hello.drl文件以下:ide
package rules.testword rule "test001" when //這裏若是爲空,則表示eval(true) then System.out.println("hello word"); end
包路徑,引用,規則體 (其中包路徑和規則體是必須的)
package:
包路徑,該路徑是邏輯路徑(能夠隨便寫,可是不能不寫,最好和文件目錄同名,以(.)的方式隔開),規則文件中永遠是第一行。
rule:
規則體,以rule開頭,以end結尾,每一個文件能夠包含多個rule ,規則體分爲3個部分:LHS,RHS,屬性 三大部分。
LHS:
(Left Hand Side),條件部分,在一個規則當中「when」和「then」中間的部分就是LHS部分,在LHS當中,能夠包含0~N個條件,若是 LHS 爲空的話,那麼引擎會自動添加一個eval(true)的條件,因爲該條件老是返回true,因此LHS爲空的規則老是返回true。
RHS:
(Right Hand Side),在一個規則中「then」後面的部分就是RHS,只有在LHS的全部條件都知足的狀況下,RHS部分纔會執行。RHS部分是規則真正作事情的部分,知足條件觸發動做的操做部分,在RHS可使用LHS部分當中的定義的綁定變量名,設置的全局變量、或者是直接編寫的java代碼,可使用import的類。不建議有條件判斷。工具
Drools是一款基於Java的開源規則引擎,因此 Drools 文件語法徹底兼容 java 語法,以 .drl
爲後綴。大體形式以下:學習
package droolsexample // list any import classes here. import com.sample.ItemCity; import java.math.BigDecimal; // declare any global variables here dialect "java" /* 規則1 */ rule "Pune Medicine Item" when item : ItemCity (purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.MEDICINES) then BigDecimal tax = new BigDecimal(0.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end /** 規則2 */ rule "Pune Groceries Item" when item : ItemCity(purchaseCity == ItemCity.City.PUNE, typeofItem == ItemCity.Type.GROCERIES) then BigDecimal tax = new BigDecimal(2.0); item.setLocalTax(tax.multiply(item.getSellPrice())); end
和 java 文件相似,先聲明包名,而後導入相關的類。一個規則由:
rule "xxx" when xxx then xxx end
這樣的形式構成。其中單行註釋以 //
開頭,多行註釋形如 /* */
。
檢測 .drl 文件中 ( )
、 { }
、 " "
是否成對出現。若是出現錯誤,指出錯誤行數。
利用 棧 的數據結構來進行檢測。
首先咱們須要給出一個空棧,而後把待檢測的代碼中的字符一一入棧,在入棧的過程當中,若是字符是一個開放符號a(也就是咱們的左括號),則把它壓入棧中,若是是一個封閉符號(右括號)b,則此時先判斷一下棧是否爲空,若是爲空的話,則報錯(也就是待檢測的代碼中的括號不一一對應),若是棧不爲空,則比較b和棧頂元素a,若是該封閉字符b和a字符匹配(也就是他們的括號可以匹配),則彈出棧頂元素,若是不匹配,則報錯。當吧全部待檢測的代碼所有迭代完後,此時若是棧不爲空,則報錯。
上面的思路中,咱們還要將註釋中的符號排除在外。
一、讀取 drl 文件內容爲字符串。
二、經過正則匹配,將 ://
替換爲其它不受影響的字符或者字符串 ,避免誤判斷。
三、經過正則匹配,將 //
單行註釋替換爲空格。正則表達式爲 //.*
四、經過正則匹配,將 /*
和 */
替換爲不受影響的字符,如 #
。(純粹是爲了計算出錯行數)
五、藉助棧的數據結構,進行判斷。
# coding=utf-8 import re def remove_annotation(file_path): with open(file_path, "r", encoding="UTF-8") as f: text = f.read() re_uri = "://" text1 = re.sub(re_uri, "_____", text) re_single = "//.*" text2 = re.sub(re_single, " ", text1) re_multi1 = "/\*" text3 = re.sub(re_multi1, "#", text2) re_multi2 = "\*/" result = re.sub(re_multi2, "#", text3) return result def check_syntax(text): try: clist = [] row_num = 1 for c in text: if c == '\n': row_num = row_num + 1 if not clist or (clist[-2] != '\"' and clist[-2] != '#'): if c == '\"': clist.append(c) clist.append(row_num) if c == '#': clist.append(c) clist.append(row_num) if c == '(': clist.append(c) clist.append(row_num) if c == '{': clist.append(c) clist.append(row_num) if c == ')': if clist[-2] == '(': clist.pop() clist.pop() else: print("多餘的 ) , 可能錯誤行數爲 " + str(row_num)) return -1 if c == '}': if clist[-2] == '{': clist.pop() clist.pop() else: print("多餘的 } , 可能錯誤行數爲 " + str(row_num)) return -1 else: if c == '\"': if clist[-2] == '\"': clist.pop() clist.pop() if c == '#': if clist[-2] == '#': clist.pop() clist.pop() if clist: print("存在多餘的 ( 或者 { 或者 \" 可能的錯誤行數爲:" + str(clist[-1])) return -2 else: print("語法檢查初步正確!") return 0 except IndexError: print("存在多餘的 ) 或者 } 或者 \" 可能的錯誤行數爲: " + str(row_num)) return -1 except Exception as e: print(e) print("其它錯誤!") drl_path = input("請輸入 drools 文件路徑,可拖拽:") content = remove_annotation(drl_path) check_syntax(content)
drools 文件就取用上面的例子,命名爲 test.drl
,如圖:
上面的腳本保存爲 check_drl.py
文件。
結果以下:
下面將故意將 drools 文件第 40 行增長一個 )
,以下圖:
再次測試以下圖:
本文簡單介紹了一下 Drools 規則引擎的使用場景以及 drool 文件的簡單語法檢測,主要是利用了棧數據結構後進先出的思想。原本是計劃用 java 來寫這個工具的,後來想了一下,仍是以爲 python 比較實在,有不少優點,列舉一二:
python 的列表 list 直接能夠代替 java 的 Stack<E>
;
python 結合正則表達式,處理字符串更方便;
python 獲取 list 倒數第二個元素,能夠直接使用 list[-2]
,java 可能須要 stack.get(stack.size()-2)
; ...