SymPy
Pythonで記号処理、数式処理をする場合はSymPyを使うのが定番かと思います。Mathematicaみたく、代数的な微分積分とか、微分方程式を解くことが出来る すごいソフトです。
前回のコードだとlambda地獄になるので、SymPyにご活躍願って、条件式などを(もうちょっとだけ)すっきり記述できないかと思ったのですが、
from sympy import * x,y=symbols("x y") #これは 「数式として」処理される」 >>> x>y x > y #あとで具体的な値を代入することもできる >>> (x>y).subs({"x":2,"y":1}) True #2重等号は その場で処理されちゃう >>> x==y False
条件分岐の判定には2重等号も数式として処理してほしかったのですが。(たぶん、大人の事情で、その場で処理しないと困ることがると思われ)
アドホックにパッチあてて遊ぶには内部構造が複雑だったので、必要な分づつマネして実装することにしました。
(微分積分とか そういうのまで やる気は無いので)
とりあえず紛失防止のため、できたとこまでメモ
Pythonオブジェクトの演算子をオーバーライドして、数式を書くとLispみたいなS式に変換する。。。だけのコード
あと、都合で、S式 (ConsList)をPythonの組み込みリストっぽく使うためのメソッドが少し付けてあります。
ここから もう一度 Pythonに戻したり*1とか クラスの継承関係とかが まだ適当なので 手を入れたり、いろいろ書かないといけないのですが
実行サンプル
x,y,z=symbols("x y z") >>> x+y*z [ + x [ * y z ] ] >>> x x >>> x==y [ == x y ] #二重等号も数式として処理 >>> a=ConsList([x,y,z]) >>> a.car x >>> a.cdr [ y z ] >>> list(a) #Pythonの組み込みリストに型変換 [x, y, z] >>> b=ConsList([x,y,z]) >>> a==b [ == [ x y z ] [ x y z ] ] >>> list(a)==list(b) True
class MyBase(object): pass class Atom(MyBase): def __init__(self,name): self.name=name def __repr__(self): return self.name class Expr(MyBase): pass class Cons(Expr): def __init__(self,car=None,cdr=None): self.car=car self.cdr=cdr def __getitem__(self,x): if not isinstance(x,(int,long)): raise IndexError if x>=0: for i,c in enumerate(self): if i==x: return c else: raise IndexError elif x<0: imax=len(self) for i,c in enumerate(self): if (i-imax)==x: return c else: raise IndexError def __len__(self): for i,c in enumerate(self): pass return i+1 def __iter__(self): cur=self while 1: yield cur.car if cur.cdr is None: raise StopIteration else : cur=cur.cdr def __repr__(self): ret=["["] for i in self: ret.append(repr(i)) ret.append("]") return " ".join(ret) def tail(self): for cur in self: pass return cur def extend(self,seq): if isinstance(seq,Cons): self.tail().cdr=seq else: cur=self for i in seq: cur.cdr=Cons(i,None) cur=cur.cdr return self class ConsList(Cons): def __init__(self,seq=[]): Cons.__init__(self) try: cur=self cur.car=seq[0] self.extend(seq[1:]) except IndexError as e: pass class Func(Atom): def __init__(self,name): self.name=name def __call__(self,*args): seq=[self].extend(args) return ConsList(seq) class Op(Func): def __init__(self,sym,spname): self.sym=sym self.name=spname def __repr__(self): return self.sym def __call__(self,x,y): getattr(x,spname)(y) optxt="""+ __add__ - __sub__ * __mul__ / __div__ == __eq__ < __lt__ > __gt__ % __mod__""" def defop(cls,op): def _(self,y): return ConsList([op,self,y]) return _ for line in optxt.splitlines(): sym,spname=line.split(" ") op=Op(sym,spname) setattr(Op,spname,op) setattr(MyBase,spname,defop(MyBase,op)) def symbols(names): return [ Atom(i) for i in names.split(" ")] if __name__=="__main__": c=ConsList([0,1,2,3]) assert c.car==0 and list(c.cdr)==[1,2,3] assert len(c)==4 assert c[0]==0 and c[1]==1 and c[2]==2 and c[3]==3 assert c[-4]==0 and c[-3]==1 and c[-2]==2 and c[-1]==3 x,y,z=symbols("x y z") a=x+y assert (a[0] is Op.__add__) and (a[1] is x) and (a[2] is y) b=x+y assert list(b) ==[ Op.__add__ ,[ Op.__mul__,x,y],z]