GoFのデザインパターン(Design Pattern)のひとつ、インタプリタ(Interpreter)をRubyのサンプルコードで紹介します。 インタプリタパターンはひとつひとつの問題はシンプルだが、組み合わさって複雑になるような場合に効果を発揮します。
🏀 インタプリタとは? 専用の言語を作り、その言語で得られた手順にもとづいて処理を実行していくデザインパターンです。
インタプリタには次の構成要素があります。
抽象表現(AbstractExpression): 共通のインタフェースを定義
終端(TerminalExpression): 終端を表現するクラス
終端以外(NonterminalExpression): 非終端を表現するクラス
状況、文脈(Context): 構文の解析を手助けする
🤔 サンプルソース サンプルとして、ファイル検索用のインタプリタを書いていきます。 まずは、すべてのファイル検索のベースとなる最も単純なクラスを作成します。
class Expression def | (other) Or.new(self , other) end def & (other) And.new(self , other) end end
続いて、すべてのファイル名を返すAll
クラスを作成します。evaluate
メソッドの概要は次のとおりです。
Rubyの標準ライブラリfindを使ってディレクトリ内のファイル名を収集
Find.findを使うことで、サブフォルダまで含めたすべてのファイルを返す
require "find" class All < Expression def evaluate (dir) results= [] Find.find(dir) do |p| next unless File.file?(p) results << p end results end end
続いて、与えられたパターンとマッチするすべてのファイル名を返すFileName
クラスを作成します。
class FileName < Expression def initialize (pattern) @pattern = pattern end def evaluate (dir) results= [] Find.find(dir) do |p| next unless File.file?(p) name = File.basename(p) results << p if File.fnmatch(@pattern, name) end results end end
さらに、指定したファイルサイズより大きいファイルを返すBigger
クラスを作成します。
class Bigger < Expression def initialize (size) @size = size end def evaluate (dir) results = [] Find.find(dir) do |p| next unless File.file?(p) results << p if ( File.size(p) > @size) end results end end
書込可能なファイルを返すWritable
クラスは次のようになります。
class Writable < Expression def evaluate (dir) results = [] Find.find(dir) do |p| next unless File.file?(p) results << p if ( File.writable?(p) ) end results end end
ここで、「書込ができないファイル」を探せるように、Writable
クラスを否定できるNot
クラスを作ります。Not
クラスは、Bigger
クラスにも適用できるので、指定したファイルサイズより小さいファイルを探せます。
class Not < Expression def initialize (expression) @expression = expression end def evaluate (dir) All.new.evaluate(dir) - @expression.evaluate(dir) end end
最後に2つのファイル検索式を「Or」「And」で結合するクラスを作ります。
class Or < Expression def initialize (expression1, expression2) @expression1 = expression1 @expression2 = expression2 end def evaluate (dir) result1 = @expression1.evaluate(dir) result2 = @expression2.evaluate(dir) (result1 + result2).sort.uniq end end class And < Expression def initialize (expression1, expression2) @expression1 = expression1 @expression2 = expression2 end def evaluate (dir) result1 = @expression1.evaluate(dir) result2 = @expression2.evaluate(dir) (result1 & result2) end end
上のコードを使ってファイルを検索した結果を下に載せました。
complex_expression1 = And.new(FileName.new('*.mp3' ), FileName.new('big*' )) puts complex_expression1.evaluate('13_test_data' ) complex_expression2 = Bigger.new(1024 ) puts complex_expression2.evaluate('13_test_data' ) complex_expression3 = FileName.new('*.mp3' ) & FileName.new('big*' ) puts complex_expression3.evaluate('13_test_data' ) complex_expression4 = All.new puts complex_expression4.evaluate('13_test_data' )
このように単純なインタプリタでも十分にファイル検索を実現できていることがわかります。
🚕 サンプルソース
🎂 参考リンク
🖥 VULTRおすすめ
「VULTR 」はVPSサーバのサービスです。日本にリージョンがあり、最安は512MBで2.5ドル/月($0.004/時間)で借りることができます。4GBメモリでも月20ドルです。
最近はVULTR のヘビーユーザーになので、「ここ 」から会員登録してもらえるとサービス開発が捗ります!