Java.use(better, Python)《13》for と別れる50の方法(その肆)
|記事一覧|《こちらに移動中です》2006年6月21日 (水)
Java.use(better, Python) # Stairway to Real Agile World
《13》for と別れる50の方法(その肆)《Jython2.5.0》
■ 概要
継承の概念を実現するのに、いくつかの方法があります。そのひとつが、新しいクラスを定義した後で、そのインスタンスに固有のメソッドを再定義するというものです。
継承には、
(1)構造継承
(2)機能継承
(3)プロトコル継承
があって、さらに(2)機能継承は、次の3つに分類されます。
(2a)親子関係にあるクラス間の継承
(2b)親子関係にないクラス間の継承
(2c)クラスとインスタンス間の継承
ともすると、Java/C# などでは、狭義の(2a)に関心が寄せられがちです。しかし、純粋な OOP の世界では、その限りではありません。では、広義の継承は、どのように実現するのでしょうか。
《13》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 の構造を表現した文字列が出力されます。
print ">>>", tree print "size :", tree.size() print "height:", tree.height() # -------------------------------- Output -- >>> 0(1(2(,),3(,)),4(5(,),6(,))) size : 7 height: 2
式 tree.size() を評価すると、二分木 tree を構成する節の数が得られます。出力結果を見ると、7つの節が存在することが分ります。式 tree.height() を評価すると、二分木 tree の高さが得られます。出力結果を見ると、高さが2であることが分ります。
※ 空の部分木を表す変数名を null としたのは作為的で、Java で記述したコードとの混乱を来すことを期待しています。また、Jython/Python における None とは、無関係な存在です。□
class Tree: def height(self): return self._height()-1
メソッド関数 height は、二分木の高さを得ます。ここでは、下請けのメソッド関数 _height を利用しています。このとき、得られた値から1を引いたものをリターンとします。
class Tree: def _height(self): return 1 + max( self.left._height(), self.right._height())
メソッド関数 _height は、height を補助するもので、他の目的で利用することはありません。部分木の高さを得るための情報を獲得します。左右の部分木 self.left および self.right に対して、_height を再帰的に呼び出すとともに、これらの最大値に 1 を加えます。よって、部分木を持たないなら、その値は 1 となります。つまり、実際の二分木の高さより、1つだけ大きな値が得られます。
※ 賢明な読者のみなさんは、部分木を持たない状況で、null が介在しているのに気付くでしょう。□
《課題》何がいけないのか
メソッド関数 height を次のように実現すると、不都合が生じます。その理由を説明してください。
class Tree: def height(self): return 0 + max( self.left.height(), self.right.height())□
二分木を構成する節を要素とするシーケンスを使うと、コレクションフレームワークで規定された、便利な操作を利用できます。
s = tree.asSequence() print ">>>", s print s.iterate(0, lambda elem,acc: acc+elem) # -------------------------------- Output -- >>> Sequence {2, 1, 3, 0, 5, 4, 6} 21
式 tree.asSequence() を評価すると、二分木を構成する節を要素とするシーケンスが得られます。メソッド関数 iterate を使うと、各節の値を累積した値が得られます。出力結果を見ると、0 から 6 までの総和 21 が得られたことが分ります。
class Tree: def asSequence(self): return self.inOrder()
メソッド関数 asSequence は、二分木を構成する節を要素とするシーケンスを生成します。ここでは、inOrder を利用して、二分木を間順走査して得られた節を要素とする、シーケンスをリターンオブジェクトとします。
コレクションフレームワークを利用すると便利です。しかし、各節を走査する順序(前順走査、間順走査、後順走査、深さ優先、幅優先)など、二分木に期待する制御構造を、利用者が自由に定義できるようになると、もっと便利です。
class Tree: pass null = Tree() null.__str__ = lambda : "" null.displayString = lambda n: "" null.size = lambda : 0 null._height = lambda : 0 null.preOrder = lambda : OCL_Sequence() null.inOrder = lambda : OCL_Sequence() null.postOrder = lambda : OCL_Sequence() null.asSequence = lambda : OCL_Sequence()
Tree のインスタンスである null には、他のインスタンスにはない固有のメソッドを定義しておきます。null は、空の部分木を表すので、必要なメソッド関数が規定されています。そのため、コードを複雑にするだけの、不要な if 文を一掃できます。すると、例外を気にすることなく、簡潔なコードを記述できるようになります。
※ preOrder、inOrder、postOrder、asSequence の詳細は、他で解説します。□
==================================
後藤いるか+河野かえる 著 ◆ 監修:小泉ひよ子とタマゴ倶楽部