python 設計模式之解釋器(Interpreter)模式

#寫在前面

關於解釋器模式,我在網上轉了兩三圈,心中有了那麼一點概念 ,也不知道本身理解的是對仍是錯。html

其實關於每一種設計模式,我總想找出一個答案,那就是爲何要用這種設計模式, 若是不用會怎麼樣,會致使什麼後果?python

我想只有搞懂了這個答案,我纔有可能會學明白,才能給出實例。正則表達式

下面開始落筆,沉澱出我對解釋器模式的印象。express

 

 

#到底什麼是解釋器模式?

這都是從下面的連接裏面摘的,我感受利於我理解,我就放在博客裏。編程

 

 爲人處事是一門大學問,察言觀色、聽懂弦外之音都是很是重要的。設計模式

 

老闆跟你說「XX你最近表現平平啊,還得要多努力」,數據結構

若是你不當回事,日常對待,可能下次就是「XX,恩,你人仍是不錯,日常工做也很努力,可是我想這份工做可能不是很適合你…..」。app

 

又好比你老大說「XX,你最近表現不錯,工做積極性很高啊!繼續保持啊!」,編程語言

你高興樂呵着心想是否是老闆要給我加工資了,可能你等到花都謝了也沒有,獲得的可能會是更多的工做量。工具

 

對於咱們剛剛入社會的人不夠圓滑,不會察言觀色,更聽不懂老闆的弦外之音,因此咱們期待若是有一個翻譯機該多好,直接將別人的弦外之音給翻譯出來就行了。
      在咱們實際的生活中是這樣,在軟件的世界裏也一樣存在着翻譯機,只不過在軟件中咱們稱之爲解釋器。在系統中若是某一特定類型的問題在頻繁的發生,此時咱們就有必要將這些問題的實例表述爲一個語言中句子,所以能夠構建一個解釋器,而後利用該解釋器來解釋這些句子來解決這些問題。

 

 #到底什麼是解釋器模式?

對每一個應用來講,至少有如下兩種不一樣的用戶分類。

1] 基本用戶:這類用戶只但願可以憑直覺使用應用。他們不喜歡花太多時間配置或學習應用的內部。對他們來講,基本的用法就足夠了。

2] 高級用戶:這些用戶,實際上一般是少數,不介意花費額外的時間學習如何使用應用的高級特性。若是知道學會以後能獲得如下好處,他們甚至會去學習一種配置(或腳本)語言。

    a]可以更好地控制一個應用

    b]以更好的方式表達想法

    c]提升生產力

解釋器(Interpreter)模式僅能引發應用的高級用戶的興趣。這是由於解釋器模式背後的主要思想是讓非初級用戶和領域專家使用一門簡單的語言來表達想種簡單的語言法。然而,什麼是一種簡單的語言?對於咱們的需求來講,一就是沒編程語言那麼複雜的語言。

通常而言,咱們想要建立的是一種領域特定語言(Domain Specific Language,DSL)。DSL是一種針對一個特定領域的有限表達能力的計算機語言。不少不一樣的事情都使用DSL,好比,戰鬥模擬、記帳、可視化、配置、通訊協議等。DSL分爲內部DSL和外部DSL。

內部DSL構建在一種宿主編程語言之上。內部DSL的一個例子是,使用Python解決線性方程組的一種語言。使用內部DSL的優點是咱們沒必要擔憂建立、編譯及解析語法,由於這些已經被宿主語言解決掉了。劣勢是會受限於宿主語言的特性。若是宿主語言不具有這些特性,構建一種表達能力強、簡潔並且優美的內部DSL是富有挑戰性的。

外部DSL不依賴某種宿主語言。DSL的建立者能夠決定語言的方方面面(語法、句法等),但也要負責爲其建立一個解析器和編譯器。爲一種新語言建立解析器和編譯器是一個很是複雜、長期而又痛苦的過程。

解釋器模式僅與內部DSL相關。所以,咱們的目標是使用宿主語言提供的特性構建一種簡單但有用的語言,在這裏,宿主語言是Python。注意,解釋器根本不處理語言解析,它假設咱們已經有某種便利形式的解析好的數據,能夠是抽象語法樹(abstract syntax tree,AST)或任何其餘好用的數據結構。

 

 

 #到底什麼是解釋器模式?

 所謂解釋器(Interpreter)就是將一系列指令轉化成代碼,可以執行的代碼。Interpreter原本就有翻譯的意思。

GoF給它的定義是:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

這很像命令模式,可是命令模式的命令必須是實現了Command的類的實例,而解釋器模式的命令則能夠是任何對象,如字符串,整數等,只要解釋器能將其解釋成具體行爲就行。

 

  #到底什麼是解釋器模式?

雖然目前計算機編程語言有好幾百種,但有時人們仍是但願用一些簡單的語言來實現特定的操做,只須要向計算機輸入一個句子或文件,就能按照預約的文法規則來對句子或文件進行解釋。例如,咱們想要只輸入一個加法/減法表達式,它就可以計算出表達式結果。例如輸入「1+2+3-4+1」時,將輸出計算結果爲3。像C++,Java或C#都沒法直接解釋相似這樣的字符串,所以用戶必須自定義一套文法規則來實現對這些語句的解釋,即設計一個自定義語言。若是所基於的編程語言是面嚮對象語言,此時可使用解釋器模式實現自定義語言。

 

#哪些地方用到解釋器模式了?

1]第一個能想到的利用解釋器模式的程序應該就是編譯器了,編譯器能將源代碼翻譯成機器能認識的機器碼,這是很典型的解釋器模式。

2]另外正則表達式(RegExp)也是解釋器模式的一個應用。

其它的領域使用解釋器模式是極少的,使用極其有限,另外有了yacc和lex等語法分析工具後使用解釋器模式的地方就更少了。

 

#優勢

1]易於改變和擴展文法 => 經過繼承來改變或擴展
2]增長新的解釋表達式較爲方便 => 只需對應新增一個新的終結符或非終結符表達式,原有代碼無須修改,符合開閉原則!

 

 #缺點

執行效率較低。解釋器模式中一般使用大量的循環和遞歸調用,當要解釋的句子較複雜時,其運行速度很慢,且代碼的調試過程也比較麻煩。
會引發類膨脹。解釋器模式中的每條規則至少須要定義一個類,當包含的文法規則不少時,類的個數將急劇增長,致使系統難以管理與維護。
可應用的場景比較少。在軟件開發中,須要定義語言文法的應用實例很是少,因此這種模式不多被使用到。

 

#應用場景

在軟件開發中,會遇到有些問題屢次重複出現,並且有必定的類似性和規律性。

其餘幾乎用不到

 

#解釋器模式角色介紹

Context:上下文環境,包含解釋器以外的全局信息
Client:客戶端,解析表達式,構建語法樹,執行具體的解釋操做等
AbstractExpression:抽象表達式,聲明一個抽象的解釋操做弗雷,並定義一個抽象的解釋方案,其具體的實如今各個具體的子類解釋器中完成。
TerminalExpression:終結符表達式,實現文法中終結符有關的解釋操做。文法中每個終結符都有一個具體的終結表達式與之對應。
NonterminalExpression:非終結表達式,實現文法中非終結符有關的解釋操做。
其中AbstractExpression的interpret()是抽象的解析方法,參數是上下文的環境,而interpret()方法的具體實現則由TerminalExpression和NonterminalExpression實現。

 

#結構圖

 

 

#解釋器模式主要包含如下幾個類

from abc import ABCMeta, abstractmethod

class AbstractExpression():
    """
    抽象表達式類,聲明一個抽象的解釋操做,這個接口爲抽象語法樹中全部的節點所共享
    """
    __metaclass__ = ABCMeta
    
    @abstractmethod
    def interpret(self, context):
        pass
    
class TerminalExpression(AbstractExpression):
    """
    終結符表達式,實現與文法中的終結符相關聯的解釋操做。實現抽象表達式中所要求的接口,主要是一個interpret()方法。
    文法中的每個終結符都有一個具體終結符表達式與之相對應。
    """
    def interpret(self, context):
        print("終結符表達式")
        
        
class NontermialExpression(AbstractExpression):
    """
    非終結符表達式,爲文法中的非終結符實現解釋操做。對文法中每一條規則R1,R2,...Rn都須要一個具體的非終結符表達式類。
    經過實現抽閒表達式的interpret()方法實現解釋操做。解釋操做以遞歸的方式調用上 main所提到的表明R1,R2,...Rn中各
    個符號的實例變量。
    """
    def interpret(self, context):
        print("非終結符表達式")
        
        
class Context():
    """
    上下文類,包含解釋器以外的一些全局信息
    """
    def __init__(self):
        self.input = None
        self.output = None
        
        
def main():
    """
    客戶端代碼,構建表示該文法定義的語言中一個特定的句子的抽象語法樹。
    """
    context = Context()
    exp_list = []
    exp_list.append(TerminalExpression())
    exp_list.append(NontermialExpression())
    exp_list.append(TerminalExpression())
    exp_list.append(NontermialExpression())
    
    for exp in exp_list:
        exp.interpret(context)
        
main()

 

 

 

 

 

#舉個例子

class Variables(object):  # Context
    def __init__(self):
        self._v = {}
    def put(self, variable, value: int):
        self._v[variable] = value
    def get(self, variable) -> int:
        return self._v.get(variable)

class ArithmeticExpression(object):  # AbstractExpression
    def interpret(self, variables) -> float:
        pass

class Variable(ArithmeticExpression):  # TerminalExpression
    def __init__(self, value: str):
        self.variable = value
    def interpret(self, variables):
        return variables.get(self.variable)

class Plus(ArithmeticExpression):  # NonTerminalExpression
    def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression):
        self._left = left
        self._right = right
    def interpret(self, variables: Variables):
        return self._left.interpret(variables) + self._right.interpret(variables)

class Substract(ArithmeticExpression):  # NonTerminalExpression
    def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression):
        self._left = left
        self._right = right
    def interpret(self, variables: Variables):
        return self._left.interpret(variables) - self._right.interpret(variables)

class Multiply(ArithmeticExpression):  # NonTerminalExpression
    def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression):
        self._left = left
        self._right = right
    def interpret(self, variables: Variables):
        return self._left.interpret(variables) * self._right.interpret(variables)

class Division(ArithmeticExpression):  # NonTerminalExpression
    def __init__(self, left: ArithmeticExpression, right: ArithmeticExpression):
        self._left = left
        self._right = right
    def interpret(self, variables: Variables):
        return self._left.interpret(variables) / self._right.interpret(variables)



class Calculator(object):
    def __init__(self, expression: str, context: Variables):
        self.expression = expression
        self.context = context
        self.value = None
    def get_value(self):
        self.calculate()
        return self.value
    def calculate(self):
        OP = ['+', '-', '*', '/', '(', ')', '=']
        priority = [  # 各運算符相遇時,優先級比較 1: 大於,2: 小於,3: 多彈一個符號
            [1, 1, 2, 2, 2, 1, 1],
            [1, 1, 2, 2, 2, 1, 1],
            [1, 1, 1, 1, 2, 1, 1],
            [1, 1, 1, 1, 2, 1, 1],
            [2, 2, 2, 2, 2, 3, 0],
            [1, 1, 1, 1, 0, 1, 1],
            [2, 2, 2, 2, 2, 0, 3]
        ]
        opan = []  # 操做數
        opat = ['=']  # 操做符,初始帶一個=爲的是做爲結束符使用
        for char in self.expression:
            while True:
                if char not in OP:  # 變量
                    opan.append(Variable(char))
                    break
                else:  # 操做符
                    level = priority[OP.index(opat[-1::1][0])][OP.index(char)]
                    if level == 1:
                        op = opat.pop()
                        b = opan.pop()
                        a = opan.pop()
                        if op == '+':
                            opan.append(Plus(a, b))
                        elif op == '-':
                            opan.append(Substract(a, b))
                        elif op == '*':
                            opan.append(Multiply(a, b))
                        elif op == '/':
                            opan.append(Division(a, b))
                    elif level == 2:
                        opat.append(char)
                        break
                    elif level == 3:
                        opat.pop()
                        break
                    else:
                        break
        self.value = opan.pop().interpret(self.context)


if __name__ == '__main__':
    variables = Variables()
    variables.put('a', 1)
    variables.put('b', 2)
    variables.put('c', 3)
    variables.put('d', 4)
    aa = Variable('a')
    bb = Variable('b')
    cc = Variable('c')
    dd = Variable('d')
    # (1-2)+(3*4)
    print(Plus(Substract(aa, bb), Multiply(cc, dd)).interpret(variables))
    print(Calculator('a-b+c*d=', variables).get_value())

 

 

 

 

 這是簡書上的一個例子, https://www.jianshu.com/p/45153c25c29c

感謝做者

後面取priority 那塊我也沒太看懂,也不影響做爲解釋器模式的例子

這裏就做參考

 

參考:

https://www.cnblogs.com/hujingnb/p/10171605.html

https://www.cnblogs.com/cbf4life/archive/2009/12/17/1626125.html

https://www.e-learn.cn/content/qita/708812

https://blog.csdn.net/hbu_pig/article/details/80808881

https://www.jianshu.com/p/d9365606fd8f

https://www.cnblogs.com/Siny0/p/11155954.html

https://www.cnblogs.com/imakoo/articles/2944578.html

https://www.cnblogs.com/chenssy/p/3679190.html

https://www.cnblogs.com/chenssy/p/3346427.html

https://www.cnblogs.com/edisonchou/p/7512733.html

https://www.jianshu.com/p/0e7e26bbe204

https://www.cnblogs.com/CheeseZH/p/9491324.html

相關文章
相關標籤/搜索