なんかデザインパターンぽいことをしつつインタプリタを書こうと思ったので。正直自分でもあまり理解できてない気がする。書いたコードのメリットもいまいち掴みきれてない。Stateパターン、あとメソッドチェーンのテクニックを(適切かどうかは置いといて)使っています。Interpreterも練習してみたかったし一部のアイデアは今回のコードに盛り込んだつもりだけど、多分Interpreterにはなってないです。どっちかというとFacadeとかに近いのかもしれない。
なんかトリッキーにやろうとしてごちゃごちゃしてる感じ。
とりあえず目的はCSA棋譜のインタプリタを書くことですが、まだ途中段階です。1行の中身(トークン単位)まで踏み込んで、CSAフォーマット仕様に準拠しているかどうかはチェックしません。CSAでは、例えば指し手表記より後に開始局面の表記があっちゃいけないなど、行(が属しているセクション)の順番を規程してます。今回書いたコードは、ポリモーフィズムとか委譲(デリゲート)とか使いながらその辺のチェックをOO的にやろうとするのが狙いです。 その行がフォーマット仕様においてどのセクションを表すのか、それを「状態」としてクラスにします。で、当然状態がとこなればAcceptできる行(セクション)は異なるわけです。よって、「状態」クラスにiterpret()メソッドを持たせ、「状態」に依存する振る舞いであるinterpret=解釈のやり方をサブクラスの実装に任せるようにします。この辺はStateパターンが入ってます。
「状態」を表すクラスは"State"クラスとその配下です。コードでは初期状態(最初の行がinterpret()された時の状態),状態1,状態2,状態3が取りうる全ての状態としています。
ポイント?としては、状態は連結リストの構造をしていることです。for文で解釈の対象となる文章の各行についてのループを回し、Stateを得るわけですが、メソッドチェーンと委譲を用いることで自動的にStateが(状態を遷移するごとに)自分の前の状態を参照に持つような連結リストとして繋がっていくようにしました。結果を得る場合は前状態の参照をたどって逆順にすればOKです。現状、クライアント側には連結リストとなったStateを返すような形になります。以下実装。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
class Interpreter:
def __init__(self, context):
self.cn = context
def interpret(self):
status = SIni()
for s in self.cn:
print "======================================="
status.setCurrent(s)
status = status.interpret()
return status
class State:
SONE=1
STWO=2
STHREE=3
OWN=-1
#status=[S1,S2,S3]
#status={SONE:S1, STWO:S2, STHREE:S3}
def __init__(self, current=None):
if(self.__class__.__name__=="State"):
raise Exception,"abstract class \"State\""
self.current = current
self.prestate = None
def setCurrent(self, s):
self.current = s
return self
def setPrestate(self, pre):
self.prestate = pre
return self
def getStateCode(self):
if(self.current == State.SONE): return State.SONE
elif(self.current == State.STWO): return State.STWO
elif(self.current == State.STHREE): return State.STHREE
else:
raise Exception, "SyntaxError"
def isMatch(self):
#s = (self.line)
#print "isMatch(): "+str(self.__class__.OWN)
if(self.getStateCode()==self.__class__.OWN):
#print "T"
return True
#print "F"
return False
def delegate(self):
#print " DELEGATE *** "+StateFactory.Create(self.getStateCode(), self.current).__class__.__name__
#cstate = self
#return StateFactory.Create(self.getStateCode(), self.current).setPrestate(cstate).interpret()
s = StateFactory.Create(self.getStateCode(), self.current)
s.setPrestate(self)
print "DELEGATE: "+s.prestate.tostring()
return s.interpret()
def interpret(self):
if(self.isMatch()):
# do approximate processing....
return self
else:
return self.delegate()
i
def tolist(self):
ret = [self]
elm = self
while not elm.prestate is None:
ret.insert(0,elm.prestate)
elm = elm.prestate
return ret
def __iter__(self):
for el in self.tolist():
yield el
def tostring(self):
return "State"+str(self.__class__.OWN)
class SIni(State):
OWN=0
def interpret(self):
if(not self.isMatch()):
return self.delegate()
class S1(State):
OWN=1
def interpret(self):
if(self.isMatch()):
print "! STATE(1) printing by "+self.__class__.__name__
print " > pre: "+self.prestate.__class__.__name__
return self
else:
return self.delegate()
class S2(State):
OWN=2
def interpret(self):
if(self.isMatch()):
print "# state[2] printing by "+self.__class__.__name__
print " > pre: "+self.prestate.__class__.__name__
return self
else:
return self.delegate()
class S3(State):
OWN=3
def interpret(self):
if(self.isMatch()):
print "% sTaTe{3} printing by "+self.__class__.__name__
print " > pre: "+self.prestate.__class__.__name__
return self
else:
return self.delegate()
class StateFactory:
status={State.SONE:S1, State.STWO:S2, State.STHREE:S3}
@classmethod
def Create(cls, scode, current):
return StateFactory.status[scode](current)
def test():
lis = [1,2,3,2,3]
intp = Interpreter(context=lis)
result = intp.interpret()
print result.prestate
print "\n"
#for elm in result.tolist():
for elm in result:
print elm.__class__.__name__
#while not result.prestate is None:
# print result.tostring()
# result = result.prestate
if __name__=="__main__":
test()
そして実行結果。
=======================================
DELEGATE: State0
! STATE(1) printing by S1
> pre: SIni
=======================================
DELEGATE: State1
# state[2] printing by S2
> pre: S1
=======================================
DELEGATE: State2
% sTaTe{3} printing by S3
> pre: S2
=======================================
DELEGATE: State3
# state[2] printing by S2
> pre: S3
=======================================
DELEGATE: State2
% sTaTe{3} printing by S3
> pre: S2
<__main__.S2 instance at 0x7f0a44f36b90>
SIni
S1
S2
S3
S2
S3
状態の遷移も一応ちゃんとできてます。で、Stateとそのサブクラスは解釈したい行の「大分類」にあたるクラスです。interpret()の中身で内部的、「小分類」的な状態管理や遷移条件を記述することでもうちょい繊細な動作も実現できるはず。「大分類」に関するコードが各サブクラスに分離できるあたりはメリットと言えるかもしれません。