Java.use(better, Python)《14》for と別れる50の方法(その伍)
|記事一覧|《こちらに移動中です》2006年6月22日 (木)
Java.use(better, Python) # Stairway to Real Agile World
《14》for と別れる50の方法(その伍)《Jython2.5.0》
■ 概要
継承の概念を実現するのに、いくつかの方法があります。そのひとつが、新しいクラスを定義した後で、そのインスタンスに固有のメソッドを再定義するというものです。
継承には、
(1)構造継承
(2)機能継承
(3)プロトコル継承
があって、さらに(2)機能継承は、次の3つに分類されます。
(2a)親子関係にあるクラス間の継承
(2b)親子関係にないクラス間の継承
(2c)クラスとインスタンス間の継承
ともすると、Java/C# などでは、狭義の(2a)に関心が寄せられがちです。しかし、純粋な OOP の世界では、その限りではありません。では、広義の継承は、どのように実現するのでしょうか。
《14》for と別れる50の方法(その伍)
■ 二分木の例
単純な二分木を使って、for 文を任意のインスタンスに適用する事例を紹介します。
tree = Tree(0,Tree(1,Tree(2),Tree(3)),Tree(4,Tree(5),Tree(6))) tree.display() # -------------------------------- Output -- +-- 2 +-- 1 +-- 3 +-- 0 +-- 5 +-- 4 +-- 6
クラス Tree は、二分木を実現したものです。Tree のインスタンスを生成して、変数 tree に代入します。tree は、いくつかの部分木によって構成されます。式 tree.display() を評価すると、tree の構造を表現した文字列が出力されます。
for node in tree: print node*10, # -------------------------------- Output -- 20 10 30 0 50 40 60
for 文を使うと、制御変数 node には、二分木 tree が保持する節が順に代入されます。出力結果を見ると、各節の値を 10 倍したものが、空白で区切られているのが分ります。
class Tree: items = None def __getitem__(self,index): if self.items == None: self.items = self.inOrder() return self.items._list[index]
メソッド関数 __getitem__ を再定義すると、任意のインスタンスに対して、[ ] を伴う添字付けが可能となります。また、インスタンスを構成する各要素を参照するのに、for 文が使えるようになります。引数 index には、各要素を参照するための添字が渡されます。ここでは、インスタンス属性 self.items には、間順走査によって得られた節を要素とする、リスト self.inOrder() が代入されています。そして、添字 index によって参照されるリスト内の要素を、リターンオブジェクトとします。
《Note》特殊メソッド:__getitem__
__getitem__(self, key)式 self[key] を評価するときに実行されるコードを実現します。シーケンス型に対して、キーとして認められるのは、整数およびスライスです。□
class Tree: def inOrder(self): l = self.left. inOrder() v = OCL_Sequence([self.value]) r = self.right.inOrder() result = l.union(v).union(r) assert self._post_asSequence(result) return result
※ OCL_Sequence の詳細については、関連する連載記事を参照してください。□
print ">>> 3:", 3 in tree print ">>> 7:", 7 in tree # -------------------------------- Output -- >>> 3: True >>> 7: False
__getitem__ を再定義するだけで、任意のインスタンスに対して、演算子 in を伴う論理演算が可能となります。ここでは、3 および 7 を値に持つ節が存在するかを判定しています。出力結果を見ると、3 を値に持つ節が存在するので True が、7 を値に持つ節が存在しないので False が、それぞれ得られているのが分ります。
print ">>>", t print "pre :", t.preOrder() print "in :", t.inOrder() print "post:", t.postOrder() # -------------------------------- Output -- >>> 0(1(2(,),3(,)),4(5(,),6(,))) pre : Sequence {0, 1, 2, 3, 4, 5, 6} in : Sequence {2, 1, 3, 0, 5, 4, 6} post: Sequence {2, 3, 1, 5, 6, 4, 0}
確かに、for 文が使えるのは便利です。しかし、間順走査だけに限定されるのでは不便です。他に、前順走査、後順走査、深さ優先、幅優先など、多様な制御構造が期待される状況でも、その中のひとつだけを for 文に規定することになります。
class Tree: def preOrder(self): l = self.left. preOrder() v = OCL_Sequence([self.value]) r = self.right.preOrder() result = v.union(l).union(r) assert self._post_asSequence(result) return result
メソッド関数 preOrder は、二分木の各節を前順走査します。
class Tree: def postOrder(self): l = self.left. postOrder() v = OCL_Sequence([self.value]) r = self.right.postOrder() result = l.union(r).union(v) assert self._post_asSequence(result) return result
メソッド関数 postOrder は、二分木の各節を後順走査します。
t = Tree("sunday", Tree("monday", Tree("tuesday"), Tree("wednesday")), Tree("thursday", Tree("friday"), Tree("saturday"))) s = t.asSequence() print ">>>", s print s.collect(lambda e: e.capitalize()) print s.select(lambda e: e.startswith("s")) print s.reject(lambda e: len(e)>6) # -------------------------------- Output -- +-- tuesday +-- monday +-- wednesday +-- sunday +-- friday +-- thursday +-- saturday >>> Sequence {'tuesday', 'monday', 'wednesday', 'sunday', 'friday', 'thursday', 'saturday'} Sequence {'Tuesday', 'Monday', 'Wednesday', 'Sunday', 'Friday', 'Thursday', 'Saturday'} Sequence {'sunday', 'saturday'} Sequence {'monday', 'sunday', 'friday'}
メソッド関数 asSequence を使うと、コレクションフレームワークで規定された、より柔軟な記述ができるようになります。しかし、これらはシーケンスに対する操作であって、二分木に対する制御構造を規定したものではありません。for 文と別れるには、さらにもうひと工夫が必要です。
==================================
後藤いるか+河野めだか 著 ◆ 監修:小泉ひよ子とタマゴ倶楽部