1
3 """If your trimmer funcion raises this exception, then the input
4 to nice_hash.add() will be ignored."""
5
8
9
13
14
15
16
18 __doc__ = """This class generates a map from inputs to integers,
19 where an equivalence class is defined by the trimmer function.
20 It is just a stripped-down version of the L{hash} class
21 defined above. Faster and less memory consumption, if
22 you don't need the rmap method.
23 """
24
25
26
28 """
29 @param trimmer: Defines equivalence classes on list;
30 it can be some sort of projection operation.
31 The default (None) makes the identity class equal to the inputs.
32 It can also specify which inputs to ignore by raising the
33 L{DontHashThis} exception.
34 """
35 self.trimmed_seen = {}
36 self.n = 0
37 if trimmer is None:
38 def trimmer(x):
39 return x
40 self.trimmer = trimmer
41
42
44 """Returns an integer which is shared among all x in the
45 equivalence class, but different from all other x.
46 If the trimmer function raises the DontHashThis exception,
47 it will ignore the input and return None.
48 @returns: int
49 """
50
51 try:
52 xt = self.trimmer(x)
53 except DontHashThis:
54 return None
55
56 try:
57 i = self.trimmed_seen[xt]
58 except KeyError:
59 i = self.n
60 self.trimmed_seen[xt] = i
61 self.n += 1
62 return i
63
64
66 """Similar to add, except it returns None if the equivalence
67 class has already appeared.
68 @returns: int or None
69 """
70
71 try:
72 xt = self.trimmer(x)
73 except DontHashThis:
74 return None
75
76 if xt in self.trimmed_seen:
77 return None
78
79 i = self.n
80 self.trimmed_seen[xt] = i
81 self.n += 1
82 return i
83
84
86 """Returns an integer which is shared among all x in the
87 equivalence class, but different from all other x.
88 @note: the return value is the same as L{add}, except that
89 this doesn't add anything: rather than increasing the
90 size of the mapping, it raises NotInHash.
91 @raise NotInHash: when x is not in the hash table.
92 """
93
94 try:
95 xt = self.trimmer(x)
96 except DontHashThis:
97 raise NotInHash('Trimmer raised DontHashThis')
98 try:
99 i = self.trimmed_seen[xt]
100 except KeyError:
101 raise NotInHash('Key: %s' % str(xt))
102 return i
103
104
106 """Return the map from classes (as returned by the trimmer function)
107 to integers."""
108 return self.trimmed_seen.copy()
109
110
112 """Return a list over all the classes that have been seen."""
113 return list(self.trimmed_seen.keys())
114
115
117 """Return the map from integers to classes.
118 @return: [classname, classsname, ...]
119 """
120 o = [None] * len(self.trimmed_seen)
121 for (x, i) in self.trimmed_seen.iteritems():
122 o[i] = x
123 return o
124
125
128 """This adds the L{rmap} method to the L{simple} class.
129 """
130 simple.__init__(self, trimmer)
131 self.seen = []
132
133
135 """Returns the map from integers to inputs."""
136
137 o = [ [] for q in self.trimmed_seen.keys() ]
138 for (x, cl) in self.seen:
139 o[self.trimmed_seen[cl]].append(x)
140 return o
141
142
144 map = self.map()
145 rmap = self.rmap()
146 for (intg, inpt) in rmap.iteritems():
147 for s in inpt:
148 assert map[s] == intg
149
151 """Returns an integer which is shared among all x in the
152 equivalence class, but different from all other x.
153 If the trimmer function raises the DontHashThis exception,
154 it will ignore the input and return None.
155 Note that if you add an item twice, it will appear twice in rmap().
156 @returns: int
157 """
158
159 try:
160 xt = self.trimmer(x)
161 except DontHashThis:
162 return None
163 self.seen.append((x, xt))
164
165 try:
166 i = self.trimmed_seen[xt]
167 except KeyError:
168 i = self.n
169 self.trimmed_seen[xt] = i
170 self.n += 1
171 return i
172
173
175 """Similar to add, except it returns None if the equivalence
176 class has already appeared.
177
178 The list of classes seen (self.seen) then
179 contains only the first item that was added in each class,
180 so L{rmap}() will produce a dictionary containing single-item lists:
181 C{{int1:[item1],int2:[item2],int3:[item3], ...}}.
182 Calls to L{add}() and L{add_newonly}() can be intermixed safely,
183 though the result of L{rmap}() will then not contain all added items.
184 @returns: int or None
185 """
186
187 try:
188 xt = self.trimmer(x)
189 except DontHashThis:
190 return None
191
192 if xt in self.trimmed_seen:
193 return None
194
195 self.seen.append((x, xt))
196 i = self.n
197 self.trimmed_seen[xt] = i
198 self.n += 1
199 return i
200
201
202
203 nice_hash = hash
204
205
206 -def map(list, trimmer=lambda x:x):
211
212
213
214 if __name__ == '__main___':
215 h = nice_hash(lambda x : x)
216 assert h.add('foo') == 0
217 assert h.add('bar') == 1
218 assert h.add('gleep') == 2
219 assert h.add('bar') == 1
220 h.test()
221 assert h.map()['bar'] == 1
222 assert h.rmap()[0] == ['foo']
223 assert h.classmap()[1] == 'bar'
224