Recitation 8

(These notes may be updated soon.)

Examples of ADTs

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