ブラウンツリー (Braun Trees) は、次のような再帰的な定義によって導かれる特殊な二分木です:
空の二分木があるとき、そのツリーはブラウンツリーです。
二分木の2つの子がブラウンツリーで、なおかつ左の子のサイズから右の子のサイズを引いたとき 0 もしくは 1 が得られるなら、その二分木はブラウンツリーです。
次のような二分木を表わす多相データ型を宣言します:
datatype tree (a:t@ype) = | tree_nil of ((*void*)) | tree_cons of (a, tree(a)(*left*), tree(a)(*right*)) // end of [tree] // end of [datatype]
fun{ a:t@ype } size (t: tree a): int = case+ t of | tree_nil () => 0 | tree_cons (_, tl, tr) => 1 + size(tl) + size(tr) // end of [size] fun{ a:t@ype } brauntest0 (t: tree a): bool = ( case+ t of | tree_nil () => true | tree_cons (_, tl, tr) => let val cond1 = brauntest0(tl) andalso brauntest0(tr) in if cond1 then let val df = size(tl) - size(tr) in (df = 0) orelse (df = 1) end else false end // end of [tree_cons] ) (* end of [brauntest0] *)
次のプログラムで定義している brauntest1 関数もまた与えられた二分木がブラウンツリーか判定しています:
fun{ a:t@ype } brauntest1 (t: tree a): bool = let exception Negative of () fun aux (t: tree a): int = ( case+ t of | tree_nil () => 0 | tree_cons (_, tl, tr) => let val szl = aux (tl) and szr = aux (tr) val df = szl - szr in if df = 0 orelse df = 1 then 1+szl+szr else $raise Negative() end // end of [tree_cons] ) (* end of [aux] *) in try let val _ = aux (t) in true // [t] is a Braun tree end with ~Negative() => false // [t] is not a Braun tree // end of [try] end // end of [brauntest1]
brauntest1 の実装における例外機構の使い方は妥当なものです。 なぜなら例外が発生するポイントから例外が捕捉されるポイントまでの範囲に多くの関数呼び出しがあるからです。 もしこの範囲が短かい場合 (例えばたった1つの関数しか呼び出さないなど)、おそらくプログラマは例外機構を使うべきか吟味すべきでしょう。 例えば、次の例における例外の使い方は興味深いものですが、実際には役に立ちません:
fun{ a:t@ype } list0_length (xs: list0 (a)): int = try 1 + list0_length (xs.tail()) with ~ListSubscriptExn() => 0 // end of [list0_length]
この章に出てくるコードの全体とテストのための追加コードは オンライン から得られます。