x >= 0 の整数が割り当てられた時、x の平方根は i * i <= x を満たすような最大の整数 i になります。 次のように平方根関数を二分探索法をもとに実装します:
fun isqrt ( x: int ) : int = let // fun search ( x: int, l: int, r: int ) : int = let val diff = r - l in case+ 0 of | _ when diff > 0 => let val m = l + (diff / 2) in // x < m * m can overflow easily if x / m < m then search (x, l, m) else search (x, m, r) // end of [if] end // end of [if] | _ (* diff <= 0 *) => l (* the result is found *) end // end of [search] // in search (x, 0, x) end // end of [isqrt]
search(x, l, r) が呼び出された時、l * l <= x かつ x <= r * r です。
整数 x, l, r について l * l <= x < r * r を仮定します。 search(x, l, r) の本体で整数 l1 と r1 について再帰関数 search(x, l1, r1) を呼び出すと、r1-l1 < r-l です。 この不変条件は search が停止することを示しています。
fun isqrt {x:nat} ( x: int x ) : int = let // fun search {x,l,r:nat | l < r} .<r-l>. ( x: int x, l: int l, r: int r ) : int = let val diff = r - l in case+ 0 of | _ when diff > 1 => let val m = l + half(diff) in if x / m < m then search (x, l, m) else search (x, m, r) // end of [if] end // end of [if] | _ (* diff <= 1 *) => l (* the result is found *) end // end of [search] // in if x > 0 then search (x, 0, x) else 0 end // end of [isqrt]
正確であろうとし、また効果的に正確さを強制できることで、実行時デバッグの必要性はどんどん小さくなることに、プログラマは確実に気が付くでしょう。