(These notes may be updated soon.)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
signature FRACTION = sig (* A fraction is a rational number *) type fraction (* first argument is numerator, second is denominator *) val make : int -> int -> fraction val numerator : fraction -> int val denominator : fraction -> int val toString : fraction -> string val toReal : fraction -> real val add : fraction -> fraction -> fraction val mul : fraction -> fraction -> fraction end (* Here's one implementation of fractions -- what can go wrong here? *) structure Fraction1 :> FRACTION = struct type fraction = { num:int, denom:int } fun make (n:int) (d:int) = {num=n, denom=d} fun numerator(x:fraction):int = #num x fun denominator(x:fraction):int = #denom x fun toString(x:fraction):string = (Int.toString (numerator x)) ^ "/" ^ (Int.toString (denominator x)) fun toReal(x:fraction):real = (Real.fromInt (numerator x)) / (Real.fromInt (denominator x)) fun mul (x:fraction) (y:fraction) : fraction = make ((numerator x)*(numerator y)) ((denominator x)*(denominator y)) fun add (x:fraction) (y:fraction) : fraction = make ((numerator x)*(denominator y) + (numerator y)*(denominator x)) ((denominator x)*(denominator y)) end (* First, we could give 0 as the denominator -- this is a bad fraction. * Second, we're not reducing to smallest form. So we could overflow * faster than we need to. * Third, we're not consistent with the signs of the numbers. Try * make ~1 ~1. * * We need to pick some representation invariant * that describes how we're going to represent legal fractions. * Here is one choice that tries to fix the bugs above. *) structure Fraction2 :> FRACTION = struct (* Rep invariant: * (1) denom is always positive * (2) always in most reduced form *) type fraction = { num:int, denom:int } (* Algorithm due to Euclid: for positive numbers x and y, * find the greatest-common-divisor. *) fun gcd (x:int) (y:int) : int = if (x = y) then x else if (x < y) then gcd x (y - x) else gcd (x - y) y exception BadDenominator fun make (n:int) (d:int) : fraction = if (d < 0) then raise BadDenominator else let val g = gcd (abs n) (abs d) val n2 = n div g val d2 = d div g in if (d2 < 0) then {num = ~n2, denom = ~d2} else {num = n2, denom = d2} end fun numerator(x:fraction):int = #num x fun denominator(x:fraction):int = #denom x fun toString(x:fraction):string = (Int.toString (numerator x)) ^ "/" ^ (Int.toString (denominator x)) fun toReal(x:fraction):real = (Real.fromInt (numerator x)) / (Real.fromInt (denominator x)) (* notice that we didn't have to re-code mul or add -- * they automatically get reduced because we called * make instead of building the data structure directly. *) fun mul (x:fraction) (y:fraction) : fraction = make ((numerator x)*(numerator y)) ((denominator x)*(denominator y)) fun add (x:fraction) (y:fraction) : fraction = make ((numerator x)*(denominator y) + (numerator y)*(denominator x)) ((denominator x)*(denominator y)) end (* Here's a signature for dictionaries. A dictionary is a container * that holds keys and associated objects. These dictionaries are * polymorphic (i.e., we can use them for any type.) Here we've * defined keys to be strings. *) signature DICTIONARY = sig type key = string type 'a dict (* make an empty dictionary carrying 'a values *) val make : unit -> 'a dict (* insert a key and value into the dictionary *) val insert : 'a dict -> key -> 'a -> 'a dict (* lookup a key in the dictionary -- raise NotFound if the * key is not present. *) val lookup : 'a dict -> key -> 'a exception NotFound end (* One implementation: an association list [(key1,x1),...,(keyn,xn)] *) structure AssocList :> DICTIONARY = struct type key = string type 'a dict = (key * 'a) list fun make():'a dict = [] fun insert (d:'a dict) (k:key) (x:'a) : 'a dict = (k,x)::d exception NotFound fun lookup (d:'a dict) (k:key) : 'a = case d of [] => raise NotFound | ((k',x)::rest) => if (k = k') then x else lookup rest k end (* This implementation seems a little better for looking up values *) structure SortedAssocList :> DICTIONARY = struct type key = string (* rep invariant: the list is sorted by key and * each key occurs only once in the list. *) type 'a dict = (key * 'a) list fun make():'a dict = [] fun insert (d:'a dict) (k:key) (x:'a) : 'a dict = case d of [] => (k,x)::nil | (k',x')::rest => (case String.compare(k,k') of GREATER => (k',x')::(insert rest k x) | EQUAL => (k,x)::rest | LESS => (k,x)::(k',x')::rest) exception NotFound fun lookup (d:'a dict) (k:key) : 'a = case d of [] => raise NotFound | ((k',x)::rest) => (case String.compare(k,k') of EQUAL => x | LESS => raise NotFound | GREATER => lookup rest k) end (* This one uses a binary tree to keep the data -- the hope is * that inserts or lookups will be proportional to log(n) where * n is the number of items in the tree. *) structure AssocTree :> DICTIONARY = struct type key = string (* Invariant: for Nodes, data to the left have keys that * are LESS than the datum and the keys of * the data to the right. *) datatype 'a dict = Empty | Node of {key: key,datum: 'a, left: 'a dict,right: 'a dict} fun make():'a dict = Empty fun insert (d:'a dict) (k:key) (x:'a) : 'a dict = case d of Empty => Node{key=k, datum=x, left=Empty, right=Empty} | Node {key=k', datum=x', left=l, right=r} => (case String.compare(k,k') of EQUAL => Node{key=k, datum=x, left=l, right=r} | LESS => Node{key=k',datum=x',left=insert l k x, right=r} | RIGHT => Node{key=k',datum=x',left=l, right=insert r k x}) exception NotFound fun lookup (d:'a dict) (k:key) : 'a = case d of Empty => raise NotFound | Node{key=k',datum=x, left=l, right=r} => (case String.compare(k,k') of EQUAL => x | LESS => lookup l k | RIGHT => lookup r k) end |