bgneal@12
|
1 """ Code example from Complexity and Computation, a book about
|
bgneal@12
|
2 exploring complexity science with Python. Available free from
|
bgneal@12
|
3
|
bgneal@12
|
4 http://greenteapress.com/complexity
|
bgneal@12
|
5
|
bgneal@12
|
6 Copyright 2011 Allen B. Downey.
|
bgneal@12
|
7 Distributed under the GNU General Public License at gnu.org/licenses/gpl.html.
|
bgneal@12
|
8 """
|
bgneal@12
|
9
|
bgneal@12
|
10 import string
|
bgneal@12
|
11
|
bgneal@12
|
12
|
bgneal@12
|
13 class LinearMap(object):
|
bgneal@12
|
14 """A simple implementation of a map using a list of tuples
|
bgneal@12
|
15 where each tuple is a key-value pair."""
|
bgneal@12
|
16
|
bgneal@12
|
17 def __init__(self):
|
bgneal@12
|
18 self.items = []
|
bgneal@12
|
19
|
bgneal@12
|
20 def add(self, k, v):
|
bgneal@12
|
21 """Adds a new item that maps from key (k) to value (v).
|
bgneal@12
|
22 Assumes that they keys are unique."""
|
bgneal@12
|
23 self.items.append((k, v))
|
bgneal@12
|
24
|
bgneal@12
|
25 def get(self, k):
|
bgneal@12
|
26 """Looks up the key (k) and returns the corresponding value,
|
bgneal@12
|
27 or raises KeyError if the key is not found."""
|
bgneal@12
|
28 for key, val in self.items:
|
bgneal@12
|
29 if key == k:
|
bgneal@12
|
30 return val
|
bgneal@12
|
31 raise KeyError
|
bgneal@12
|
32
|
bgneal@12
|
33
|
bgneal@12
|
34 class BetterMap(object):
|
bgneal@12
|
35 """A faster implementation of a map using a list of LinearMaps
|
bgneal@12
|
36 and the built-in function hash() to determine which LinearMap
|
bgneal@12
|
37 to put each key into."""
|
bgneal@12
|
38
|
bgneal@12
|
39 def __init__(self, n=100):
|
bgneal@12
|
40 """Appends (n) LinearMaps onto (self)."""
|
bgneal@12
|
41 self.maps = []
|
bgneal@12
|
42 for i in range(n):
|
bgneal@12
|
43 self.maps.append(LinearMap())
|
bgneal@12
|
44
|
bgneal@12
|
45 def find_map(self, k):
|
bgneal@12
|
46 """Finds the right LinearMap for key (k)."""
|
bgneal@12
|
47 index = hash(k) % len(self.maps)
|
bgneal@12
|
48 return self.maps[index]
|
bgneal@12
|
49
|
bgneal@12
|
50 def add(self, k, v):
|
bgneal@12
|
51 """Adds a new item to the appropriate LinearMap for key (k)."""
|
bgneal@12
|
52 m = self.find_map(k)
|
bgneal@12
|
53 m.add(k, v)
|
bgneal@12
|
54
|
bgneal@12
|
55 def get(self, k):
|
bgneal@12
|
56 """Finds the right LinearMap for key (k) and looks up (k) in it."""
|
bgneal@12
|
57 m = self.find_map(k)
|
bgneal@12
|
58 return m.get(k)
|
bgneal@12
|
59
|
bgneal@12
|
60
|
bgneal@12
|
61 class HashMap(object):
|
bgneal@12
|
62 """An implementation of a hashtable using a BetterMap
|
bgneal@12
|
63 that grows so that the number of items never exceeds the number
|
bgneal@12
|
64 of LinearMaps.
|
bgneal@12
|
65
|
bgneal@12
|
66 The amortized cost of add should be O(1) provided that the
|
bgneal@12
|
67 implementation of sum in resize is linear."""
|
bgneal@12
|
68
|
bgneal@12
|
69 def __init__(self):
|
bgneal@12
|
70 """Starts with 2 LinearMaps and 0 items."""
|
bgneal@12
|
71 self.maps = BetterMap(2)
|
bgneal@12
|
72 self.num = 0
|
bgneal@12
|
73
|
bgneal@12
|
74 def get(self, k):
|
bgneal@12
|
75 """Looks up the key (k) and returns the corresponding value,
|
bgneal@12
|
76 or raises KeyError if the key is not found."""
|
bgneal@12
|
77 return self.maps.get(k)
|
bgneal@12
|
78
|
bgneal@12
|
79 def add(self, k, v):
|
bgneal@12
|
80 """Resize the map if necessary and adds the new item."""
|
bgneal@12
|
81 if self.num == len(self.maps.maps):
|
bgneal@12
|
82 self.resize()
|
bgneal@12
|
83
|
bgneal@12
|
84 self.maps.add(k, v)
|
bgneal@12
|
85 self.num += 1
|
bgneal@12
|
86
|
bgneal@12
|
87 def resize(self):
|
bgneal@12
|
88 """Makes a new map, twice as big, and rehashes the items."""
|
bgneal@12
|
89 new_maps = BetterMap(self.num * 2)
|
bgneal@12
|
90
|
bgneal@12
|
91 for m in self.maps.maps:
|
bgneal@12
|
92 for k, v in m.items:
|
bgneal@12
|
93 new_maps.add(k, v)
|
bgneal@12
|
94
|
bgneal@12
|
95 self.maps = new_maps
|
bgneal@12
|
96
|
bgneal@12
|
97
|
bgneal@12
|
98 def main(script):
|
bgneal@12
|
99 m = HashMap()
|
bgneal@12
|
100 s = string.ascii_lowercase
|
bgneal@12
|
101
|
bgneal@12
|
102 for k, v in enumerate(s):
|
bgneal@12
|
103 m.add(k, v)
|
bgneal@12
|
104
|
bgneal@12
|
105 for k in range(len(s)):
|
bgneal@12
|
106 print k, m.get(k)
|
bgneal@12
|
107
|
bgneal@12
|
108
|
bgneal@12
|
109 if __name__ == '__main__':
|
bgneal@12
|
110 import sys
|
bgneal@12
|
111 main(*sys.argv)
|