-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathagingcache.py
More file actions
160 lines (141 loc) · 4.35 KB
/
agingcache.py
File metadata and controls
160 lines (141 loc) · 4.35 KB
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
"""
http://blog.lukego.com/blog/2013/02/04/cute-code/
Let's see what the Python looks like.
untested
"""
new, old = {}, {}
def get(k):
return new[k] if k in new else put(k, old.get(k))
def put(k, v):
if v is not None: new[k] = v
return v
def age():
global new, old
new, old = {}, new
# Simpler
capacity = 1000
table = {}
get = table.get
def put(k, v):
if len(table) == capacity and k not in table:
table.clear()
table[k] = v
# Fancier, but good only for small capacities:
class MRUDict(dict):
"A dict with fixed capacity, keeping only the most recently used keys."
def __init__(self, capacity=5):
dict.__init__(self)
self.capacity = capacity
self._keys = [] # Most-recently-touched first.
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __getitem__(self, key):
self.touch(key)
return dict.__getitem__(self, key)
def __setitem__(self, key, value):
self.touch(key)
return dict.__setitem__(self, key, value)
def get(self, key, default=None):
if key in self: self.touch(key)
return dict.get(self, key, default)
def touch(self, key):
if key in self: self._keys.remove(key)
self._keys.insert(0, key)
if self.capacity < len(self._keys):
dict.__delitem__(self, self._keys.pop())
## d = MRUDict(2)
## d[1] = 'x'
## d[2] = 'y'
## d
#. {1: 'x', 2: 'y'}
## d[3] = 'z'
## d
#. {2: 'y', 3: 'z'}
## d[2]
#. 'y'
## d
#. {2: 'y', 3: 'z'}
## d[1] = 'x'
## d
#. {1: 'x', 2: 'y'}
# Fancier still
# See also http://code.activestate.com/recipes/577970-simplified-lru-cache/
P, S, K, V = range(4) # Fields in a cache entry: predecessor, successor, key, value
class MRUCache(object):
def __init__(self, capacity=5):
self.capacity = capacity
self.map = {}
self.head = [None, None, None, None]
self.head[P] = self.head[S] = self.head # For a circular list
self.self_check()
def _entries(self):
entry = self.head[S]
while entry is not self.head:
yield entry
entry = entry[S]
def __repr__(self):
return repr([(entry[K], entry[V]) for entry in self._entries()])
def self_check(self):
assert len(self.map) <= self.capacity
assert len(self.map) == sum(1 for _ in self._entries())
for key in self.map:
assert any(entry[K] == key for entry in self._entries())
for entry in self._entries():
assert entry[P] is not entry
assert entry[S] is not entry
assert entry[P][S] is entry
assert entry[S][P] is entry
assert entry[K] in self.map
def __contains__(self, key):
return key in self.map
def get(self, key, default=None):
return self.touch(key, self.map[key][V]) if key in self.map else default
def __setitem__(self, key, value):
self.touch(key, value)
def touch(self, key, value):
self.self_check()
if key in self.map:
self._remove(self.map[key])
pred, succ = self.head, self.head[S]
self.map[key] = pred[S] = succ[P] = [pred, succ, key, value]
if self.capacity < len(self.map):
self._remove(self.head[P])
self.self_check()
return value
def _remove(self, entry):
del self.map[entry[K]]
entry[P][S], entry[S][P] = entry[S], entry[P]
def touch(self, key, value):
# Like previous touch() but with fewer map lookups and trickier code.
self.self_check()
try:
entry = self.map[key]
except KeyError:
(pred, succ) = (self.head, self.head[S])
pred[S] = succ[P] = self.map[key] = [pred, succ, key, value]
else:
(entry[S][P], entry[P][S]) = (entry[P], entry[S])
(entry[P], entry[S]) = (pred, succ) = (self.head, self.head[S])
pred[S] = succ[P] = entry
if self.capacity < len(self.map):
oldest = self.head[P]
(oldest[S][P], oldest[P][S]) = (oldest[P], oldest[S])
del self.map[oldest[K]]
self.self_check()
return value
## d = MRUCache(2)
## d[1] = 'x'
## d[2] = 'y'
## d
#. [(2, 'y'), (1, 'x')]
## d[3] = 'z'
## d
#. [(3, 'z'), (2, 'y')]
## d.get(2)
#. 'y'
## d
#. [(2, 'y'), (3, 'z')]
## d[1] = 'x'
## d
#. [(1, 'x'), (2, 'y')]