デザインパターンの本を開くと、終盤に必ず登場するのがインタープリタパターンです。「いったいいつ使うの?」と思って、ざっと読み飛ばしがちなパターンですよね。
ところが、電卓アプリを作っていると、文字列で表された数式を直接計算しなければならない場面がありました。そこでインタープリタパターンがぴったりはまりました。
この記事では、Swiftでとても小さな言語処理系、つまりミニインタープリタを実際に作ります。1 + 2 * 3のような数式を読み取り、数値7を返すところまでが目標です。
先に結論を言うと、こういうことです。
インタープリタパターンは、文法規則を一つずつクラス(または列挙型)に変換し、ツリー構造に組み立ててから再帰的に走査し、結果を得る方式です。
難しそうに聞こえますが、コードで見ると意外に単純です。一緒に作ってみましょう。
インタープリタパターンとは?
インタープリタパターンは、「自分だけの小さな言語」を作り、その言語を解釈する規則をコードで表現する方法です。
ここでいう言語は大げさなものではありません。数式、検索フィルターの条件、ゲームのルールスクリプトのように、決まった文法を持つ小さな表現が対象になります。
核心となるアイデアは一つだけです。
文法の各要素をオブジェクトにし、それぞれに自分自身を解釈するinterpret()メソッドを持たせます。
たとえば1 + 2という数式を考えてみましょう。ここには数値1、数値2、そして加算演算+があります。
数値は「自分の値をそのまま返す」と解釈し、加算は「左辺と右辺を解釈して、二つを足す」と解釈します。
このような小さな規則が集まってツリーを構成し、最上位で一度解釈を呼び出せば、下へたどりながら計算が完了します。
Swiftで式ツリーを設計する
Swiftでは、enumがこのパターンと非常によく合います。再帰enumを使えば、ツリー構造をすっきり表現できるからです。
最初はプロトコルとクラスで作りましたが、enumに変えるとコードが半分になりました。
まず、式を表すenumを定義します。単一の数値と、二つの式を加算または乗算するケースに分けます。
このenumは自分自身を含む再帰構造なので、indirectキーワードを付ける必要があります。
indirect enum Expr {
case number(Double) // 数値リテラル
case add(Expr, Expr) // 加算
case multiply(Expr, Expr) // 乗算
}
次に、このツリーを解釈する関数を作ります。各ケースを再帰的に処理することが、インタープリタパターンの核心です。
次の関数は式を受け取り、実際の数値を計算して返します。
func interpret(_ expr: Expr) -> Double {
switch expr {
case .number(let value):
return value
case .add(let l, let r):
return interpret(l) + interpret(r)
case .multiply(let l, let r):
return interpret(l) * interpret(r)
}
}
ここまで来れば、1 + 2 * 3をツリーに組み立てて計算できます。
.add(.number(1), .multiply(.number(2), .number(3)))のように手作業でツリーを作り、interpretに渡すと、結果として7が得られます。
文字列の数式をツリーに変換するには?
ここで疑問が一つ浮かぶでしょう。ユーザーが入力するのはツリーではなく、"1 + 2 * 3"のような文字列ですよね。
この文字列をツリーに変換する工程をパース(parsing)と呼び、それを行うコードをパーサーと呼びます。
標準的には、パーサーは二段階に分かれます。
- レクサー(lexer):文字列をトークン単位に分割する(例:
1、+、2) - パーサー(parser):トークンを文法規則に従ってツリーに組み立てる
この二段階をきちんと実装すると記事が長くなりすぎるので、ここでは概念だけ押さえて先へ進みます。
重要なのは、インタープリタパターン自体が担当するのはツリーの解釈だけだという点です。文字列をツリーに変換するパースは別の処理です。
そのため学習段階では、パーサーをいったん省略し、ツリーを手作業で作ってインタープリタを完成させることをおすすめします。そうすればパターンの本質が見えてきます。
実際にはどこで使うの?
正直なところ、インタープリタパターンをゼロから実装する機会は多くありません。複雑な言語なら、すでに完成度の高いパーサーライブラリを使うほうがはるかに適切です。
それでも、このパターンを理解するメリットは多くあります。
- 正規表現エンジンが内部でどう動くのか、感覚をつかめます
- SwiftUIの宣言型構文が、なぜツリー構造なのか理解できます
- 検索フィルターや数式計算のような小さなDSL(Domain-Specific Language、ドメイン固有言語)を作るとき、そのまま応用できます
特に、電卓や条件フィルター、ゲームスクリプトのように文法が単純で範囲が狭い場合は、自作したほうがかえってすっきりします。
このパターンを学んでから、SwiftUIのビュー構造がまったく違って見えるようになりました。ビュー一つひとつが、結局は解釈されるツリーのノードだったのです。
まとめ
今日はSwiftでミニ言語インタープリタを作りながら、インタープリタパターンを見てきました。
再帰enumで式ツリーを作り、interpret関数で再帰的に解釈すること。この二つがすべてです。
最初はなじみがなくても、実際に1 + 2 * 3を計算してみれば、「ああ、こういうことか」と腑に落ちる瞬間が来ます。今日学んだコードを、ぜひ一度自分で入力してみてください。

