File: hashMap.sml

package info (click to toggle)
mlton 20210117%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 58,464 kB
  • sloc: ansic: 27,682; sh: 4,455; asm: 3,569; lisp: 2,879; makefile: 2,347; perl: 1,169; python: 191; pascal: 68; javascript: 7
file content (154 lines) | stat: -rw-r--r-- 4,725 bytes parent folder | download | duplicates (5)
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
(*
 * map datatype that uses hashing.
 *
 * -- allen
 *)

structure HashMap :> HASH_MAP =
struct

   structure A = Array

   datatype 'a tree = NODE of 'a * 'a tree * 'a tree | EMPTY 

   datatype ('a,'b) map = 
      MAP of
      { table : ('a * 'b) tree Array.array ref,
        size  : int ref,
        order : 'a * 'a -> order,
        hash  : 'a -> int,
        exn   : exn
      }

   fun create { order, hash, exn } N =
   let val N = if N <= 10 then 10 else N
   in
      MAP { table    = ref(Array.array(N,EMPTY)),
            size     = ref 0,
            order    = order,
            hash     = hash,
            exn      = exn
          }
   end

   fun size (MAP { size, ... }) = !size

   fun bucketSize (MAP { table, ... }) = Array.length (!table)

   fun isEmpty (MAP { size, ... }) = !size = 0

   fun clear (MAP { size, table, ... }) = 
       (table := A.array(A.length(!table),EMPTY); size := 0)

   and insert (m as MAP { size, table = ref T, order, hash, exn,...})
              (e as (x,y)) = 
   let val pos = hash x mod A.length T
       fun ins EMPTY = (size := !size + 1; NODE(e,EMPTY,EMPTY))
         | ins (NODE(e' as (x',y'),l,r)) =
           case order(x,x') of
              LESS    => NODE(e',ins l,r)
           |  EQUAL   => NODE(e,l,r)
           |  GREATER => NODE(e',l,ins r)
   in  A.update(T,pos,ins(A.sub(T,pos)));
       if !size > 6 * A.length T then
          grow m
       else ()
   end

   and grow (MAP { size, table = table as ref T, order, hash, exn, ... }) =
   let val m2 as 
           MAP{table = ref T',...} = create{ order=order, hash=hash, exn=exn } 
                   (!size * 2 + 10) (* : ('a,'b) map  *)
       val ins = insert m2 
       fun loop EMPTY = ()
         | loop (NODE(e,l,r)) = (ins e; loop l; loop r)
   in  A.app loop T; table := T'
   end

   and update (m as MAP { size, table = ref T, order, hash, exn,...})
              (e as (x,y), f) = 
   let val pos = hash x mod A.length T
       fun ins EMPTY = (size := !size + 1; NODE(e,EMPTY,EMPTY))
         | ins (NODE(e' as (x',y'),l,r)) =
           case order(x,x') of
              LESS    => NODE(e',ins l,r)
           |  EQUAL   => NODE((x',f y'),l,r)
           |  GREATER => NODE(e',l,ins r)
   in  A.update(T,pos,ins(A.sub(T,pos)));
       if !size > 6 * A.length T then
          grow m
       else ()
   end

   fun remove (MAP { size, table = ref T, order, hash, exn,...}) x =
   let val pos = hash x mod A.length T
       fun del EMPTY = EMPTY
         | del (NODE(e' as (x',_),l,r)) =
           case order(x,x') of
              LESS    => NODE(e',del l,r)
           |  EQUAL   => (size := !size - 1;
                         case (l,r) of
                            (EMPTY,r) => r
                         |  (l,EMPTY) => l
                         |  _         => let val (leftmost,r') = delLeftMost r
                                         in  NODE(leftmost,l,r')
                                         end
                         )
           |  GREATER => NODE(e',l,del r)
       and delLeftMost EMPTY = raise exn
         | delLeftMost (NODE(e,EMPTY,r)) = (e,r)
         | delLeftMost (NODE(e,l,r)) = 
           let val (e',r') = delLeftMost r
           in  (e',NODE(e,l,r'))
           end
 
   in  A.update(T,pos,del(A.sub(T,pos)))
   end

   fun lookup (MAP { table = ref T, order, hash, exn, ... }) x =
   let val pos = hash x mod A.length T
       fun look EMPTY = raise exn
         | look (NODE(e' as (x',y'),l,r)) =
            case order(x,x') of
               LESS    => look l
            |  EQUAL   => y'
            |  GREATER => look r
   in  look (A.sub(T,pos))
   end

   fun lookupOrElse m default x = lookup m x handle _ => default

   fun contains (MAP { table = ref T, order, hash, ... }) x =
   let val pos = hash x mod A.length T
       fun find EMPTY = false
         | find (NODE(e' as (x',y'),l,r)) =
            case order(x,x') of
               LESS    => find l
            |  EQUAL   => true
            |  GREATER => find r
   in  find(A.sub(T,pos))
   end

   fun fold f x =
      fn (MAP { table = ref T, ... }) =>
      let fun collect (EMPTY,L)           = L
            | collect (NODE(e,l,r),L) = collect(l,collect(r,f(e,L)))
      in  A.foldl (fn (t,l) => collect(t,l)) x T
      end

   fun app f = 
      fn (MAP { table = ref T, ... }) =>
      let fun appTree EMPTY         = ()
            | appTree (NODE(e,l,r)) = (f e; appTree l; appTree r)
      in  A.app appTree T
      end

   fun toList map = fold (op::) [] map

   fun toString (f,g) map =
      "{" ^ fold (fn ((x,y),"") => "(" ^ f x ^ ", " ^ g y ^ ")"
                   | ((x,y),l)  => "(" ^ f x ^ ", " ^ g y ^ "), " ^ l
                 ) "" map ^ "}"  

end