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版♪2003/05/23

■ 概要

継承の概念を実現するのに、いくつかの方法があります。そのひとつが、新しいクラスを定義した後で、そのインスタンスに固有のメソッドを再定義するというものです。

継承には、
 (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 の詳細は、他で解説します。□

==================================
後藤いるか+河野かえる 著 ◆ 監修:小泉ひよ子とタマゴ倶楽部

Last updated♪2009/08/02