Skip to content

Commit ed3433d

Browse files
committed
refactor: separate DoublyLinkedList and LruCache to adhere to SRP
1 parent 6607118 commit ed3433d

1 file changed

Lines changed: 58 additions & 38 deletions

File tree

Lines changed: 58 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
class Node:
2+
__slots__ = ("key", "value", "next", "previous")
3+
24
# Create a building block for the cache
35
def __init__(self, key, value):
46
self.key = key
@@ -7,68 +9,86 @@ def __init__(self, key, value):
79
self.previous = None
810

911

12+
class DoublyLinkedList:
13+
# Manages node ordering (head, tail, pushing, removing, and moving to head)
14+
def __init__(self):
15+
self.head = None
16+
self.tail = None
17+
18+
def push_head(self, node):
19+
if self.head is None:
20+
self.head = node
21+
self.tail = node
22+
else:
23+
node.next = self.head
24+
self.head.previous = node
25+
self.head = node
26+
return node
27+
28+
def remove(self, node):
29+
if self.head == self.tail and node == self.head:
30+
self.head = None
31+
self.tail = None
32+
elif node == self.head:
33+
self.head = self.head.next
34+
if self.head is not None:
35+
self.head.previous = None
36+
elif node == self.tail:
37+
self.tail = self.tail.previous
38+
if self.tail is not None:
39+
self.tail.next = None
40+
else:
41+
node.previous.next = node.next
42+
node.next.previous = node.previous
43+
44+
node.next = None
45+
node.previous = None
46+
47+
def pop_tail(self):
48+
if self.tail is None:
49+
return None
50+
tail_node = self.tail
51+
self.remove(tail_node)
52+
return tail_node
53+
54+
def move_to_head(self, node):
55+
if self.head == node:
56+
return
57+
self.remove(node)
58+
self.push_head(node)
59+
60+
1061
class LruCache:
1162
# Initializes the cache with a maximum capacity
1263
def __init__(self, limit):
1364
if limit <= 0:
1465
raise ValueError("Cache limit must be greater than zero.")
1566
self.limit = limit
1667
self.cache = {} # Maps key -> Node for O(1) lookups
17-
self.head = None # Most recently used
18-
self.tail = None # Least recently used
68+
self.order_list = DoublyLinkedList() # Manages LRU order
1969

2070
# Looks up a value and marks it as recently used
2171
def get(self, key):
2272
if key not in self.cache:
2373
return None
2474

2575
node = self.cache[key]
26-
self._move_to_head(node)
76+
self.order_list.move_to_head(node)
2777
return node.value
2878

2979
# Add or update an item, evicting the oldest if full
3080
def set(self, key, value):
3181
if key in self.cache:
3282
node = self.cache[key]
3383
node.value = value
34-
self._move_to_head(node)
84+
self.order_list.move_to_head(node)
3585
return
3686

3787
if len(self.cache) >= self.limit:
38-
if self.tail is not None:
39-
del self.cache[self.tail.key]
40-
41-
if self.head == self.tail:
42-
self.head = None
43-
self.tail = None
44-
else:
45-
self.tail = self.tail.previous
46-
self.tail.next = None
88+
evicted_node = self.order_list.pop_tail()
89+
if evicted_node is not None:
90+
del self.cache[evicted_node.key]
4791

4892
new_node = Node(key, value)
4993
self.cache[key] = new_node
50-
51-
if self.head is None:
52-
self.head = new_node
53-
self.tail = new_node
54-
else:
55-
new_node.next = self.head
56-
self.head.previous = new_node
57-
self.head = new_node
58-
59-
# Internal helper to reposition a used node to the front
60-
def _move_to_head(self, node):
61-
if self.head == node:
62-
return
63-
64-
if node == self.tail:
65-
self.tail = node.previous
66-
self.tail.next = None
67-
else:
68-
node.previous.next = node.next
69-
node.next.previous = node.previous
70-
71-
node.next = self.head
72-
self.head.previous = node
73-
node.previous = None
74-
self.head = node
94+
self.order_list.push_head(new_node)

0 commit comments

Comments
 (0)