nounai.output(spaghetiThinking);

趣味と実益を兼ねて将棋プログラム(研究ツールなど)を作ってみたいと思う私の試行錯誤とか勉強したことを綴ってゆく予定です。 主目的はプログラミングの経験値稼ぎですが、コンピュータ将棋の製作も目指してみたいとも考えています。

【DesignPattern】【Interpreter?】棋譜のインタプリタに使えそうっぽいものを書いてみた【State】

なんかデザインパターンぽいことをしつつインタプリタを書こうと思ったので。正直自分でもあまり理解できてない気がする。書いたコードのメリットもいまいち掴みきれてない。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()の中身で内部的、「小分類」的な状態管理や遷移条件を記述することでもうちょい繊細な動作も実現できるはず。「大分類」に関するコードが各サブクラスに分離できるあたりはメリットと言えるかもしれません。