設計模式–解釋器模式(Interpreter Pattern)
什麼是解釋器模式?
- 解釋器模式 (Interpreter Pattern) 是一種用於當程式中有具備語言特質的物件(可能是重複、或是具備一定的規則),就可將這物件作為一個語言模型,將其經由 Laxing 拆分 以及 Parsing 解析 程序,以 將這個物件再重新轉譯成更具結構的物件 ,以 增加其擴充的彈性 ,大致就是這樣的一個設計模式。
- 解釋器模式將語言的解釋和執行過程抽象為一個類,稱為 解釋器。解釋器可以 對任意語言進行拆分(Laxing)和解析(Parsing), 適用的對象物件包括自然語言、程式語言、數學公式、正則表達式等等。
解釋器模式的 UML 架構圖
- 解釋器模式的 UML 架構圖如下所示:
classDiagram
class AbstractExpression {
+ interpret(): void
}
class TerminalExpression {
+ interpret(): void
}
class NonTerminalExpression {
+ interpret(): void
}
class Context {
+ execute(): void
}
AbstractExpression <|-- TerminalExpression
AbstractExpression <|-- NonTerminalExpression
Context o-- AbstractExpression
- 其中:
- AbstractExpression 是抽象的解釋器類別,它定義了解釋器的基本行為,即 interpret() 方法 (可參考第二則範例說明)。
- TerminalExpression 是 終端解釋器,它表示語法中的 終端符 ,即 不能再分解 的符號。
- NonTerminalExpression 是 非終端解釋器,它表示語法中的 非終端符 (表示還可再切分成更小單元),即可以 再分解 的符號。
- Context 是 上下文類別,它包含了 解釋器 所需要的環境信息。
範例說明:
- 範例1: Udemy 上老師課程上的範例
class Token:
'''
Token 類別用來表示語法分析器所產生的 token
'''
class Type(Enum):
'''
Token 的類型 Enum
'''
INTEGER = 0
PLUS = 1
MINUS = 2
LPAREN = 3
RPAREN = 4
def __init__(self, type, text):
'''
建構函數,初始化 Token 類別
'''
self.type = type
self.text = text
def __str__(self):
'''
列印 Token 時的字串表示
'''
return f'`{self.text}`'
def lex(input):
'''
lex 函式用來將輸入的字串轉換為 token 的列表
'''
result = []
i = 0
while i < len(input):
if input[i] == '+':
result.append(Token(Token.Type.PLUS, '+'))
elif input[i] == '-':
result.append(Token(Token.Type.MINUS, '-'))
elif input[i] == '(':
result.append(Token(Token.Type.LPAREN, '('))
elif input[i] == ')':
result.append(Token(Token.Type.RPAREN, ')'))
else: # must be a number
digits = [input[i]]
for j in range(i + 1, len(input)):
if input[j].isdigit():
digits.append(input[j])
i += 1
else:
result.append(Token(Token.Type.INTEGER,
''.join(digits)))
break
i += 1
return result
# ↑↑↑ 詞彙解析 ↑↑↑
# ↓↓↓ 語法解析 ↓↓↓
class Integer:
'''
Integer 類別用來表示整數節點
'''
def __init__(self, value):
self.value = value
class BinaryOperation:
'''
BinaryOperation 類別用來表示二元運算節點
'''
class Type(Enum):
ADDITION = 0
SUBTRACTION = 1
def __init__(self):
self.type = None
self.left = None
self.right = None
@property
def value(self):
if self.type == self.Type.ADDITION:
return self.left.value + self.right.value
elif self.type == self.Type.SUBTRACTION:
return self.left.value - self.right.value
def parse(tokens):
'''
將 Token 序列進行語法解析 --> 將 token 的列表轉換為語法樹
'''
result = BinaryOperation()
have_lhs = False
i = 0
while i < len(tokens):
token = tokens[i]
if token.type == Token.Type.INTEGER:
integer = Integer(int(token.text))
if not have_lhs: # lhs --> left hand side
# 將左邊的運算元設定為 integer 物件,並將 have_lhs 設定為 True。
result.left = integer
have_lhs = True
else:
# 將右邊的運算元設定為 integer 物件。
result.right = integer
elif token.type == Token.Type.PLUS:
result.type = BinaryOperation.Type.ADDITION
elif token.type == Token.Type.MINUS:
result.type = BinaryOperation.Type.SUBTRACTION
elif token.type == Token.Type.LPAREN:
# 注:沒有 RPAREN 的 if
j = i
while j < len(tokens):
if tokens[j].type == Token.Type.RPAREN:
break
j += 1
# 遞迴解析子運算式
subexpression = tokens[i + 1:j]
element = parse(subexpression)
if not have_lhs:
result.left = element
have_lhs = True
else:
result.right = element
i = j # 更新索引
i += 1
return result
def eval(input):
'''
評估運算式
'''
tokens = lex(input)
print(' '.join(map(str, tokens)))
parsed = parse(tokens)
print(f'{input} = {parsed.value}')
if __name__ == '__main__':
eval('(13+4)-(12+1)')
eval('1+(3-4)')
# 這個不會工作
eval('1+2+(3-4)')
# 執行結果:
# `(` `13` `+` `4` `)` `-` `(` `12` `+` `1` `)`
# (13+4)-(12+1) = 4
# `1` `+` `(` `3` `-` `4` `)`
# 1+(3-4) = 0
# `1` `+` `2` `+` `(` `3` `-` `4` `)`
# 1+2+(3-4) = 0
- 範例2:四則運算(加法)的 Implement
from abc import ABC, abstractmethod
# AbstractExpression
class Expression(ABC):
@abstractmethod
def interpret(self, context):
pass
# TerminalExpression for Numbers
class NumberExpression(Expression):
def __init__(self, number):
self.number = number
def interpret(self, context):
return self.number
# TerminalExpression for Variables
class VariableExpression(Expression):
def __init__(self, variable_name):
self.variable_name = variable_name
def interpret(self, context):
return context.get(self.variable_name)
# NonTerminalExpression for Addition
class AddExpression(Expression):
def __init__(self, left, right):
self.left = left
self.right = right
def interpret(self, context):
return self.left.interpret(context) + self.right.interpret(context)
# Context class
class Context:
def __init__(self):
self.variables = {}
def set(self, name, value):
self.variables[name] = value
def get(self, name):
return self.variables[name]
# Client code
if __name__ == "__main__":
# Set up the context
context = Context()
context.set("x", 5)
context.set("y", 7)
# Build the expression tree
x = VariableExpression("x")
y = VariableExpression("y")
sum = AddExpression(x, y)
# Interpret the expression
result = sum.interpret(context)
print("x + y = ", result) # Output: x + y = 12
# 執行結果:
# x + y = 12
解釋器模式的優點及缺點分析
-
優點:
- 將 語言的解釋邏輯與語言的使用邏輯分離,使語言的解釋可以 靈活擴展。
- 可以 將語言的解釋邏輯進行封裝,提高了語言的 可維護性。
-
缺點:
- 增加了語言的 複雜性。
- 可能會降低語言的執行效率。
-
適用場景:
- 領域特定語言
- 複雜輸入解釋
- 可擴展的語言結構