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