クックの分類を意識した、Python による「未定義な遷移の解釈」の実装
Diary?::2007-10-12 - (22:35) 経由で、未定義な遷移の解釈 -- 3つの立場 - 檜山正幸のキマイラ飼育記 のうち未定義がエラーを(値として…)返す場合を同じく Python で。
関数とメソッド(クラス内定義関数)の区別を持つ Python は、クックの ADT と PDA(OOP)の両方のスタイルをサポートできます(もちろん、情報隠蔽が十分でないので、いずれもかなり限定的にではありますが…)。両者の違いを際だたせるのによさそうな例に思えたので、少し遊んでみました。
クックの ADT を意識した場合
class err: def __str__(self): return "error" class n(int): pass def up(v): if isinstance(v,n): return n(v+1) elif isinstance(v,err): return v def down(v): if isinstance(v,n): if v>0: return n(v-1) else: return err() elif isinstance(v,err): return v def reset(v): return n(0) proc = lambda v,f: f(v) print reduce(proc,[n(1),up,reset]) #=> 0 print reduce(proc,[n(1),up,up,down]) #=> 2 print reduce(proc,[n(1),up,up,down,down,down,down]) #=> error
クックの PDA(OOP) を意識した場合
class Err: def __str__(self): return "error" def up(self): return self def down(self): return self def reset(self): return N(0) class N(int): def up(self): return N(self+1) def down(self): if self>0: return N(self-1) else: return Err() def reset(self): return N(0) proc = lambda v,f: getattr(v,f)() print reduce(proc,[N(1),"up","reset"]) #=> 0 print reduce(proc,[N(1),"up","up","down"]) #=> 2 print reduce(proc,[N(1),"up","up","down","down","down","down"]) #=> error
書いていて気が付いたのですが、クックはリスコフの ADT を、原義より狭い範囲に追いやってしまって、そうして空けたスペースに彼の OOP(PDA)を据えようとしたのかもしれませんね。(というのもリスコフは、後者のスタイルについても、パフォーマンス面での不利を理由に CLU での採用は見送ったものの、ADT 実装スタイルの選択肢としては完全には排除しておらず、この点で両者とも ADT の範疇であるとも解釈できるので…)
追記:
N.down() でやっている、0 以上であるという事実上の“型チェック”を N.__new__() に追い出してしまって、こんなふうに書くこともできますね。
class N(int): def __new__(cls,v): if v>=0: return int.__new__(cls,v) else: return Err() def up(self): return N(self+1) def down(self): return N(self-1) def reset(self): return N(0)