Skip to content

Commit b5c4d18

Browse files
committed
add lru cache
1 parent 531816c commit b5c4d18

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

ehco/link_list/lru.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
class Node:
2+
def __init__(self, value=None, next=None, prev=None):
3+
self.value = value
4+
self.next = next
5+
self.prev = prev
6+
7+
def __str__(self):
8+
return f"<Node: value: {self.value}>"
9+
10+
__repr__ = __str__
11+
12+
13+
class DBL:
14+
def __init__(self):
15+
node = Node("root")
16+
node.prev = node
17+
node.next = node
18+
self.root = node
19+
20+
self.lens = 0
21+
22+
@property
23+
def head(self):
24+
return self.root.next
25+
26+
@property
27+
def tail(self):
28+
return self.root.prev
29+
30+
def append(self, value):
31+
node = Node(value)
32+
33+
self.tail.next = node
34+
node.prev = self.tail
35+
self.root.prev = node
36+
self.lens += 1
37+
38+
def append_left(self, value):
39+
node = Node(value)
40+
41+
node.next = self.head
42+
self.root.next = node
43+
node.prev = self.root
44+
self.lens += 1
45+
46+
def remove(self, node):
47+
# NOTE for spec case
48+
if node == self.root:
49+
return False
50+
if node == self.tail:
51+
self.root.prev = node.prev
52+
if node.next:
53+
node.next.prev = node.prev
54+
node.prev.next = node.next
55+
del node
56+
self.lens -= 1
57+
return True
58+
59+
def iter_item(self):
60+
cur = self.root.next
61+
while cur:
62+
yield cur
63+
cur = cur.next
64+
65+
def find(self, value):
66+
for node in self.iter_item():
67+
if node.value == value:
68+
return node
69+
return None
70+
71+
def insert(self, value, new_value):
72+
per = self.root
73+
for node in self.iter_item():
74+
if node.value == value:
75+
temp = Node(new_value)
76+
per.next = temp
77+
temp.prev = per
78+
temp.next = node
79+
node.prev = temp
80+
self.lens += 1
81+
return True
82+
per = node
83+
self.append(new_value)
84+
85+
def __str__(self):
86+
return "->".join([str(node.value) for node in self.iter_item()])
87+
88+
__repr__ = __str__
89+
90+
91+
class LRU:
92+
def __init__(self, size=10):
93+
self.size = size
94+
self._link = DBL()
95+
self._cache = dict()
96+
97+
def _move_to_recent(self, node):
98+
if node == self._link.tail:
99+
return
100+
101+
# pop node from link
102+
node.prev.next = node.next
103+
node.next.prev = node.prev
104+
# set node to tail
105+
now_tail = self._link.tail
106+
now_tail.next = node
107+
node.prev = now_tail
108+
node.next = None
109+
self._link.root.prev = node
110+
111+
def _append(self, k, v):
112+
self._link.append(v)
113+
self._cache[k] = self._link.tail
114+
# Bind cache key
115+
self._link.tail.cache_key = k
116+
117+
def _expired(self):
118+
need_expired = self._link.head
119+
self._link.remove(need_expired)
120+
del self._cache[need_expired.cache_key]
121+
122+
@property
123+
def now_count(self):
124+
return self._link.lens
125+
126+
def get(self, k):
127+
node = self._cache.get(k, None)
128+
if not node:
129+
return
130+
self._move_to_recent(node)
131+
return node.value
132+
133+
def set(self, k, v):
134+
if k in self._cache:
135+
node = self._cache[k]
136+
node.value = v
137+
self._move_to_recent(node)
138+
else:
139+
if self.now_count == self.size:
140+
self._expired()
141+
self._append(k, v)
142+
return True
143+
144+
def __str__(self):
145+
return "->".join([f"{node.cache_key}" for node in self._link.iter_item()])
146+
147+
__repr__ = __str__
148+
149+
150+
def test_lru():
151+
lru = LRU(size=10)
152+
153+
for i in range(10):
154+
lru.set(i, i)
155+
156+
for i in range(10):
157+
assert lru.get(i) == i
158+
159+
print(lru)
160+
lru.set(11, 11)
161+
assert lru.get(11) == 11
162+
print(lru)
163+
164+
assert lru.get(0) == None
165+
166+
for i in range(100):
167+
lru.set(i, i)
168+
print(lru)

0 commit comments

Comments
 (0)