SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

よろずプログラマーのためのPython導入ガイド

Java meets Python - 第5回 二分木と辞書

よろずプログラマーのためのPython導入ガイド (7)

検索:イルカいるか?

 二分木に限らず、任意の要素を検索する操作が、いつか必要になります。

class Tnone(BinTree):
    def _detect(self, key): return None

class Tnode(BinTree):
    def _detect(self, key):
        if key == self.word: return self
        node = self.left ._detect(key)
        if node: return node
        node = self.right._detect(key)
        if node: return node

 補助関数_detectは、指定したキー(単語)keyを含む部分木(ノード)をリターン値とします。クラスTnoneでは、左右の部分木を持たないので、単にNoneをリターン値とします。クラスTnodeでは、二分木を構成する各ノードの中から、再帰呼び出し_detect()によって得られたノードnodeが存在するなら、そのnodeをリターン値とします。このとき、ifに続く条件式nodeの真偽を判定するのは、先の__nonzero__の責務です。これが、再帰呼び出しの終了条件にもなります。

包含:イルカいないか?

 組み込み演算子inを利用すると、任意のキーが辞書に含まれるかを判定できます。

>>> m
{'A': 65, 'C': 67, 'B': 66}
>>> "A" in m
True
>>> "@" in m
False

 2項演算子inの左項にキーを、右項に辞書を指定すると、そのキーが辞書に含まれるかどうかを判定した結果True/Falseが得られます。ここでは、キー"A"は辞書mに含まれるので、Trueが得られます。また、キー"@"は辞書mに含まれないので、Falseが得られます。

メソッド __contains__ は何処に

 メソッド__contains__は、演算子inの操作を規定します。

class Tnode(BinTree):
    def __contains__(self, key):
        return (True, False)[not self._detect(key)]

 ここでは、先の補助関数_detectを再利用しています。指定したキー(単語)keyを含む部分木(ノード)が存在するならTrueを、存在しないならFalseを、それぞれリターン値とします。

真偽値の「存在」意義とは?

>>> issubclass(bool, int)
True
>>> isinstance(True, bool), isinstance(False, bool)
(True, True)
>>> True == 1, False == 0
(True, True)

 組み込み型boolをクラスとして見なすと、intの子孫クラスにあたるので、組み込み関数issubclassによってTrueと評価されます。そのため、クラスboolのインスタンスであるTrue/Falseは、同値演算子 == を使って、任意の整数と同値比較できます。すると、True/Falseは、それぞれ 1/0 と同値なのが分かります。

存在しないものの「存在」意義とは?

>>> issubclass(type(None), object)
True
>>> (not None) == 0, (not None) == 1
(False, True)
>>> not None
True

 Noneは「存在しないこと」を表現する実体(唯一の存在 sole instance)です。「存在しない」ことの否定表現は「存在する」です。そのため、式not Noneは、Trueと評価されます。

3項演算子に代わるもの?

 先の考察から、否定演算子notを利用すると、C言語の3項演算子「?:」と同様に、式を簡潔に表現できます。

>>> ("no","yes")[not None]
'yes'

 式not Noneは、Trueと評価されるので、整数 1 と同値です。そのため、タプルの(先頭からのオフセット 1 に存在する)第2要素'yes'が得られます。つまり、False/0でないなら、True/1と評価されるので、C言語の3項演算子と等価です。

>>> ("not empty","empty")[not {}]
'empty'
>>> ("not empty","empty")[not {"a":1}]
'not empty'

 同様に、式not {}は、Trueと評価される(辞書は空である)ので、整数 1 と同値です。そのため、タプルの第2要素'empty'が得られます。つまり「否定」の否定は「肯定」となります。また、式not {"a":1}は、Falseと評価される(辞書は空でない)ので、整数 0 と同値です。そのため、タプルの第1要素'not empty'が得られます。つまり「肯定」の否定は「否定」となります。

包含:いないかイルカ?

 では実際に、これらのメソッドの動作を確認してみましょう。

>>> s = "good as happy as happy can be"
>>> p = None
>>> for e in s.split(" "): p = addtree(p, e)
>>> p.tree()
            +--
      +-- 'as'(2)
                        +--
                  +-- 'be'(1)
                        +--
            +-- 'can'(1)
                  +--
+-- 'good'(1)
            +--
      +-- 'happy'(2)
            +--

 まず、後の動作を確認するために、二分木pを生成しておきます。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> "happy" in p
True
>>> "god" in p
False

 2項演算子inの左項にキーを、右項に二分木を指定すると、そのキーが二分木に含まれるかどうかを判定した結果True/Falseが得られます。ここでは、単語"happy"は二分木pに含まれるので、Trueが得られます。また、単語"god"は二分木pに含まれないので、Falseが得られます。

例外:TypeError は語る

 ここで再び、メソッド__contains__を実現する前まで、話を戻します。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> "happy" in p
Traceback (innermost last):
...
TypeError: iteration over non-sequence

 実行時に例外オブジェクトTypeErrorを生成すると共に、エラーメッセージ「iteration over non-sequence」を出力します。これは、シーケンス(文字列、リストなど)でない対象に対して、イテレーション操作を適用しようとしたからです。

 このことから、__contains__を実現したので、イテレーション操作の対象となり、演算子inなどを適用できるようになったことが分かります。Pythonに限らず、与えられたフレームワークを効果的に活用するためには、ハリウッドの原則に従って、プログラマーが「自分からは呼び出さない」メソッド群を用意しておく必要があります。この話題については、今後の連載で紹介しますので、話を先に進めます。

包含:いるいるイルカ?

 メソッドhas_keyを利用すると、任意のキーが辞書に含まれるかを判定できます。

>>> m
{'A': 65, 'C': 67, 'B': 66}
>>> m.has_key("A")
True
>>> m.has_key("@")
False

 引数に指定したキーが、辞書に含まれるかどうかを判定した結果True/Falseが得られます。ここでは、キー"A"は辞書mに含まれるので、Trueが得られます。また、キー"@"は辞書mに含まれないので、Falseが得られます。

class Tnode(BinTree):
    def has_key(self, key):
        return key in self

 メソッドhas_keyでは、演算子inを再利用しています。つまり、演算子inを介して、メソッド__contains__に記述した処理を実行します。すると、演算子inと同じ結果が得られるようになります。

 では実際に、これらのメソッドの動作を確認してみましょう。

>>> p
good(1,as(2,,can(1,be(1,,),)),happy(2,,))
>>> p.has_key("happy")
True
>>> p.has_key("god")
False

 has_keyの引数に指定した単語が、二分木に含まれるかどうかを判定した結果True/Falseが得られます。ここでは、単語"happy"は二分木pに含まれるので、Trueが得られます。また、単語"god"は二分木pに含まれないので、Falseが得られます。

次のページ
値の獲得

この記事は参考になりましたか?

よろずプログラマーのためのPython導入ガイド連載記事一覧

もっと読む

この記事の著者

小泉ひよ子とタマゴ倶楽部(コイズミヒヨコトタマゴクラブ)

http://tamago-club.cocolog-nifty.com/「楽しくなければ仕事じゃない」が私たちのモットー。99%の苦悩の連続も、1%の成功に報われます。だからこそ、この仕事が楽しくて仕方がないのです。楽をするための努力なら惜しみません。何もせず楽をしているのと、努力をしたから楽ができるのと...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

本間 こりす(ホンマ コリス)

新人です。他のみなさんに支えられながら、マイペースで頑張ります。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1803 2008/04/04 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー