《余録》Swing/Jython - 再帰 vs. reduce

Java プログラマーのための Python 導入ガイド記事一覧
《余録》再帰 vs. reduce

《著》小粒ちゃん《監修》小泉ひよ子とタマゴ倶楽部
第1版♪2003/05/23 ● 第2版♪2009/04/03

■ 概要

フォルダー/ファイルの階層構造を Swing/GUI を利用して「簡単に」閲覧できるツールがあると便利です。

 セミナー課題では、JTree/DefaultMutableTreeNode と同等の機能を「実現する」方法を紹介しました。
 ここでは、入門者向けに、既存の機能を「利用する」方法を紹介するとともに、その問題点について考察します。

 《Note》JPython1.1.x/Jython2.1.x 用に作成したセミナー課題を、Jython2.5 で再構成しました。

事例:階乗

■ factorial:再帰

再帰呼び出しで必須になるのは「停止条件」です。「再帰呼び出しをいつ止めるか」を判定する条件が不適切だと、永遠に処理を繰り返す(無限ループに陥る)ことになります。

def fact(n):
    if n<2: return 1
    return n*fact(n-1)

関数 fact は、指定した引数 n の階乗を求めるもので、if に続く条件式 n<2 が停止条件です。

■ factorial:reduce 版

組み込み関数を利用すると、再帰呼び出しに伴う「停止条件」が不要になり、簡潔で見通しの良いコードを記述できるようになります。

def fact(n):
    return reduce(lambda s,e: s*e, range(n,1,-1), 1)

ここでは、変数 n の値が 2 未満なら、空のリストが得られるので、lambda 式を一度も実行しません。そして、式の本体にある s*e が、再帰呼び出しに相当します。

■ factorial:for..in 版

for 文でも同じ結果は得られますが、reduce と比べて、やや冗長な表現を余儀なくされます。

def fact(n):
    s = 1
    for e in range(n,1,-1):
        s *= e
    return s

ここでも、変数 n の値が 2 未満なら、for ブロックを一度も実行しません。そして、ブロック本体にある s *= e が、再帰呼び出しに相当します。

事例:課題

セミナー課題では、再帰的な構造を取り扱いたいときに、reduce を積極的に活用しています。

イテレーター:__iter__

組み込み関数 iter に呼応して、各要素を参照するイテレーターが得られます。

class DirNode(Node):                            # Composite::Composite
    def preorder(self):
        return reduce(
            lambda s,e: s+e.userObject.preorder(),
            self.node.children(),
            [self])

メソッド preorder は(preorderEnumeration と同様に)その傘下にある各ノードを「前順走査」します。

ここでも、self.node.children() が空リスト(で子を持たない)なら、lambda 式を一度も実行しません。そして、式の本体にある e.userObject.preorder() が、再帰呼び出しに相当します。

イテレーター:インデント

メソッド indents を利用すると、各ノードのインデントを返すイテレーターが得られます。

class DirNode(Node):                            # Composite::Composite
    def indents(self, indent=0):
        return reduce(
            lambda s,e: s+e.userObject.indents(indent+1),
            self.node.children(),
            [indent])

メソッド indents は、その傘下にある各ノードのインデント(深さ)を「前順走査」します。

ここでも、self.node.children() が空リスト(で子を持たない)なら、lambda 式を一度も実行しません。そして、式の本体にある e.userObject.indents(indent+1) が、再帰呼び出しに相当します。


⇒ 続きはこちら 〈GoF〉Iterator を実現する

Tips

Smalltalk では inject:into: が用意され、OCL でも同様の iterate 操作が規定されるなど、さまざまな場面で、reduce と同等の機能が要求されています。また、OCL では、iterate 操作を基軸にして、他のコレクション操作を規定しています。その一方で、Smalltalk では、do: を基軸にして、inject:into: を導出しています。

》作業中です《

Last updated♪09/05/22