Python インタプリタパターン

インタプリタパターン(Interpreter Pattern)は、ビヘイビア型デザインパターンの一種で、ある言語の文法を定義し、その言語の文を解釈するインタプリタを構築します。簡単に言えば、特定の構文規則を持つ式を解析して実行できる小さなコンパイラのようなものです。

核心理念

計算機のシナリオを想像してみてください。あなたが入力するとき、"2 + 3 * 4"このような式の場合、計算機はこの文字列の意味を理解し、数学の演算規則に従って計算を行う必要があります。インタプリタパターンは、この「理解」と「実行」のプロセスを実現する設計手法です。

適用シーン

インタプリタパターンは特に以下の場合に適しています:

  • 言語や式を解釈して実行する必要がある場合
  • 構文が比較的単純で、あまり複雑ではない場合
  • 実行効率が重要な考慮事項ではない場合
  • 言語の構文規則を頻繁に拡張する必要がある場合

パターン構造

クラス図を通して、インタプリタパターンの中心となるコンポーネントを理解しましょう。

コンポーネントの説明

AbstractExpression(抽象式)

  • 解釈操作のインターフェースを定義します
  • 通常は含みますinterpret()メソッド

TerminalExpression(終端式)

  • 文法内の終端記号に関連する解釈操作を実装します
  • それ以上小さな式に分解することはできません

NonterminalExpression(非終端式)

  • 文法内の非終端記号に関連する解釈操作を実装します
  • 通常は他の式への参照を含みます

Context(コンテキスト)

  • インタプリタが必要とするグローバルな情報を含みます
  • 入力と出力結果を格納します

基本構文と実装

抽象式クラス

例

from abc import ABC, abstractmethod

class Expression(ABC):
    """抽象式クラス。解釈インターフェースを定義します"""
   
    @abstractmethod
    def interpret(self, context):
        """解釈メソッド。具象サブクラスで実装されます"""
        pass

終端式

例

class NumberExpression(Expression):
    """数値式 - 終端式"""
   
    def __init__(self, number):
        self.number = number
   
    def interpret(self, context):
        # 数値を直接返します
        return self.number
   
    def __str__(self):
        return f"Number({self.number})"

非終端式

例

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)
   
    def __str__(self):
        return f"({self.left} + {self.right})"

class SubtractExpression(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)
   
    def __str__(self):
        return f"({self.left} - {self.right})"

class MultiplyExpression(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)
   
    def __str__(self):
        return f"({self.left} * {self.right})"

完全な例:簡易計算機

数式を解釈するための完全な簡易計算機を実装しましょう:

例

class CalculatorContext:
    """計算機コンテキスト。計算に関連する情報を格納します"""
   
    def __init__(self):
        self.variables = {}  # 変数の値を格納
   
    def set_variable(self, name, value):
        """変数の値を設定します"""
        self.variables[name] = value
   
    def get_variable(self, name):
        """変数の値を取得します"""
        return self.variables.get(name, 0)

class VariableExpression(Expression):
    """変数式 - 終端式"""
   
    def __init__(self, name):
        self.name = name
   
    def interpret(self, context):
        # コンテキストから変数の値を取得
        return context.get_variable(self.name)
   
    def __str__(self):
        return self.name

class Calculator:
    """計算機クラス - 式を構築して解釈します"""
   
    @staticmethod
    def parse_expression(expression_str, context):
        """
文字列式を式ツリーに解析します
ここでは簡略化しています。実際のアプリケーションでは、より複雑なパーサーが必要になる場合があります
        """

        簡易のな式解析(実際のプロジェクトでは専用の解析ライブラリの使用を推奨)
        tokens = expression_str.replace('(', ' ( ').replace(')', ' ) ').split()
        return Calculator._parse_tokens(tokens)
   
    @staticmethod
    def _parse_tokens(tokens):
        """トークンリストを再帰的に解析します"""
        if not tokens:
            return None
           
        token = tokens.pop(0)
       
        if token == '(':
            # 複合式の解析を開始
            left = Calculator._parse_tokens(tokens)
            operator = tokens.pop(0)
            right = Calculator._parse_tokens(tokens)
            tokens.pop(0)  # ')' を除去
           
            if operator == '+':
                return AddExpression(left, right)
            elif operator == '-':
                return SubtractExpression(left, right)
            elif operator == '*':
                return MultiplyExpression(left, right)
        else:
            # 基本式(数値または変数)を解析
            if token.isdigit() or (token[0] == '-' and token[1:].isdigit()):
                return NumberExpression(int(token))
            else:
                return VariableExpression(token)

# 使用例
def main():
    # コンテキストを作成
    context = CalculatorContext()
    context.set_variable('x', 10)
    context.set_variable('y', 5)
   
    # 式を手動で構築: (x + 3) * (y - 2)
    expression = MultiplyExpression(
        AddExpression(VariableExpression('x'), NumberExpression(3)),
        SubtractExpression(VariableExpression('y'), NumberExpression(2))
    )
   
    print(f"式: {expression}")
    result = expression.interpret(context)
    print(f"結果: {result}")  # 出力: (10 + 3) * (5 - 2) = 39
   
    # パーサーを使用
    simple_expr = AddExpression(NumberExpression(5), NumberExpression(3))
    print(f"簡易式: {simple_expr} = {simple_expr.interpret(context)}")

if __name__ == "__main__":
    main()

高度な応用:SQL WHERE 条件インタプリタ

より実用的な例を見てみましょう - 簡略化された SQL WHERE 条件インタプリタの実装:

例

class SQLContext:
    """SQL クエリコンテキスト"""
   
    def __init__(self, record):
        self.record = record  # データレコード
   
    def get_value(self, field):
        """フィールド値を取得します"""
        return self.record.get(field)

class FieldExpression(Expression):
    """フィールド式"""
   
    def __init__(self, field_name):
        self.field_name = field_name
   
    def interpret(self, context):
        return context.get_value(self.field_name)

class ConstantExpression(Expression):
    """定数式"""
   
    def __init__(self, value):
        self.value = value
   
    def interpret(self, context):
        return self.value

class EqualsExpression(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)

class AndExpression(Expression):
    """AND式"""
   
    def __init__(self, left, right):
        self.left = left
        self.right = right
   
    def interpret(self, context):
        return self.left.interpret(context) and self.right.interpret(context)

class OrExpression(Expression):
    """OR式"""
   
    def __init__(self, left, right):
        self.left = left
        self.right = right
   
    def interpret(self, context):
        return self.left.interpret(context) or self.right.interpret(context)

# 使用例:SQL WHERE 条件によるフィルタリングをシミュレート
def sql_demo():
    # データレコードをシミュレート
    records = [
        {'name': 'Alice', 'age': 25, 'department': 'Engineering'},
        {'name': 'Bob', 'age': 30, 'department': 'Sales'},
        {'name': 'Charlie', 'age': 28, 'department': 'Engineering'},
        {'name': 'Diana', 'age': 35, 'department': 'Marketing'}
    ]
   
    # WHERE 条件を構築: (department = 'Engineering') AND (age > 26)
    class GreaterThanExpression(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)
   
    # 式ツリーを構築
    condition = AndExpression(
        EqualsExpression(FieldExpression('department'), ConstantExpression('Engineering')),
        GreaterThanExpression(FieldExpression('age'), ConstantExpression(26))
    )
   
    print("条件に一致するレコード:")
    for record in records:
        context = SQLContext(record)
        if condition.interpret(context):
            print(f"- {record['name']}, {record['age']}歳, {record['department']}")

if __name__ == "__main__":
    sql_demo()

パターンの長所と短所

長所

  1. 構文の拡張が容易:新しい式クラスを追加するだけで構文規則を拡張できます
  2. 実装が容易:各式クラスは比較的単純で、実装と保守が容易です
  3. 柔軟性が高い:解釈方法を動的に変更できます
  4. オープン・クローズド原則に適合:拡張に対して開かれており、変更に対して閉じられています

短所

  1. パフォーマンスの問題:インタプリタパターンは通常、効率が低く、特に複雑な構文の場合に顕著です
  2. 複雑性の増加:複雑な構文では多数のクラスが生成され、システムの複雑さが増します
  3. デバッグが困難:複雑な式ツリーのデバッグは難しい場合があります

ベストプラクティスと注意点

使用のタイミング

  • 構文が比較的単純で安定している場合
  • 実行効率が最優先の考慮事項ではない場合
  • 新しい構文規則を頻繁に追加する必要がある場合

代替案

複雑な構文の場合は、以下の代替案を検討してください:

  • 既存のパーサージェネレーターを使用する(例:ANTLR、PLY)
  • コンパイラ/インタプリタフレームワークを使用する
  • 他のデザインパターンを検討する、例えば抽象構文木を処理するビジターパターンなど

パフォーマンス最適化テクニック

例

class OptimizedExpression(Expression):
    """最適化版の式基底クラス"""
   
    def __init__(self):
        self._cache = {}  # キャッシュメカニズムを追加
   
    def interpret(self, context):
        # キャッシュを使用して重複計算を回避
        cache_key = id(context)
        if cache_key not in self._cache:
            self._cache[cache_key] = self._do_interpret(context)
        return self._cache[cache_key]
   
    def _do_interpret(self, context):
        """実際の解釈ロジック。サブクラスで実装されます"""
        pass

実践演習

練習 1:計算機の拡張

計算機に以下の機能を追加してください:

  • 除算
  • 剰余演算
  • 括弧の優先順位のサポート

練習 2:ブール式インタプリタの実装

ブール式インタプリタを作成し、以下をサポートします:

  • AND、OR、NOT 操作
  • 比較操作(>, <, >=, <=, ==, !=)
  • 変数の置換

練習 3:ルールエンジンの設計

インタプリタパターンを使用して、以下のことができる簡単なルールエンジンを設計します:

  • ビジネスルールを解析する
  • 入力データに基づいてルールを評価する
  • 決定結果を出力する

まとめ

解釈者パターンは、特定領域言語(DSL)を処理するためのエレガントなソリューションを提供します。実際のプロジェクトでは、複雑な構文に対しては専門の解析ツールを使用する傾向がありますが、解釈者パターンの原理を理解することは、コンパイラの原理と言語処理を習得する上で重要な意義があります。

デザインパターンの核となる考え方を覚えておいてください:すべてのパターンがすべてのシナリオに適しているわけではありません。現在のニーズに最適なソリューションを選択することが鍵です。

その他の拡張