Java.use(better); Episode#22 ファインダーを作成する

前の記事記事一覧次の記事
Java.use(better, Scala);


Episode#22

ファインダーを作成する:履歴を残す



@父親が、子供達にできる最も重要なことは
@子供達の母親を愛することだ
The most important thing a father can do for his children is to love their mother.
Theodore Hesburgh - Wikipedia

《関連記事》

■ trait Iterator

特定の TreeNode を文字列で表現するために、新たなクラス Node を規定します。この目的に適う制御構造を再定義すると、コードの見通しが良くなります。

 15: import javax.swing.tree.TreeNode
 16: class Node(node: TreeNode) {
 17:   override def toString() = displayString(node, 0)
 18:   def displayString(node: TreeNode, level: Int): String = {
 19:     val buf = new StringBuffer()
 20:     buf.append("%s- %s\n".format(tabs(level), node))
 21:     nodeIterator(node) foreach { e =>
 22:       buf.append(displayString(e, level+1))
 23:     }
 24:     buf.toString
 25:   }
 26:   def tabs(level: Int) = "  "*level
 29:   def nodeIterator(node: TreeNode) = new Iterator[TreeNode] {
 30:     val iter = node.children
 31:     def hasNext = iter.hasMoreElements
 32:     def next()  = iter.nextElement.asInstanceOf[TreeNode]
 33:   }
 34: }

[17]オブジェクトを表現する文字列を作成します。メソッド displayString を下請にして、特定のノード node を頂点 0 とするツリーを文字列で表現します。

[18-25]特定のノード node を頂点 level とするツリーを文字列で表現します。[19]バッファー buf を用意して、各ノードの情報を追加します。[20]先行するタブの数 tabs(level) が、そのノードのレベルに対応します。[21]各ノードを巡回するイテレーター nodeIterator を利用して、深さ優先で探索します。[22]再帰呼び出し displayString がツリーの階層構造を反映するので、次のレベルを探索するときに、実引数の値 level+1 を増やします。

[29-33]特定のノード node の子を巡回するために、trait Iterator で規定したメソッドを実現します。[31]メソッド hasNext は、hasMoreElements に代えて、まだ巡回していないノードがあるかを判定します。[32]メソッド next は、nextElement に代えて、次に巡回するノードを獲得します。

┃余録:インスタンスを生成する

新たなインスタンスを生成するときに、それと関係の深いコードの断片だけを容易に抽出できると便利です。

■ 事例:class Person

次の事例は、その理解を深めるための便宜的なものです。

class Person(name0: String) {
  override def toString = "Person(%s%s%s)" format (name,
    if (birthday == null) "" else " %s" format birthday,  
    if (fellows.isEmpty) ""
    else ", [%s]" format fellows.mkString(", "))
  def name: String = name0
  var birthday: (Int,Int,Int) = _
  def birth(y: Int, m: Int, d: Int) = { birthday = (y,m,d) }
  import scala.collection.mutable.ListBuffer
  val fellows = new ListBuffer[Person]
  def add(p: Person) { fellows += p }
}

特定の情報を参照 name したり、新たな情報を設定 birth したり、関連するオブジェクトを登録 add するメソッドを利用すると、次のようなことができます。

val p = new Person("John Doe")
println(p.name)
p.birth(1970,1,1)
p.add(new Person("A"))
p.add(new Person("B"))

新たなインスタンス p を生成すると、その名前を参照できるので、誕生日を設定して、2人の仲間を追加します。

問題は、関係の深いコードの断片を特定するのに「変数 p だけが頼り」ということです。やがて、コードが大きく複雑になると、バグの温床になりかねません。なにか良策はないでしょうか。あります。

Scala では、インスタンスを生成するときに、そのブロック内で初期値を設定するだけでなく、任意の処理を記述できます。

val p = new Person("Jane Doe") {
  println(name)
  birth(1970,1,1)
  add(new Person("A"))
  add(new Person("B"))
}

関係の深い情報が散在せず、同じブロック内にあるだけで、コードの見通しが良くなります。どちらも、次の結果が得られます。

scala> print(p)
Person(Jane Doe (1970,1,1), [Person(A), Person(B)])

》作業中です《

 ↑ TOP

Created: 2010/05/23|Last updated: 2013/06/12 22:06:04