From ab69cdbc4b02e03f80d421e9c9797fb29d28e889 Mon Sep 17 00:00:00 2001 From: donarbl Date: Sat, 31 Jan 2026 21:31:43 +0000 Subject: [PATCH 1/3] implemented lru_cache --- Sprint-2/implement_lru_cache/lru_cache.py | 91 +++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index e69de29..b160298 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -0,0 +1,91 @@ +class _Node: + def __init__(self, key, value): + self.key = key + self.value = value + self.next = None + self.previous = None + + +class LruCache: + def __init__(self, limit): + if limit <= 0: + raise ValueError("limit must be positive") + + self.limit = limit + self.map = {} # key -> _Node + self.head = None # most recently used + self.tail = None # least recently used + + # ---- internal helpers for the linked list ---- + + def _add_to_head(self, node): + """Put node at the front (most recently used).""" + node.previous = None + node.next = self.head + + if self.head is not None: + self.head.previous = node + self.head = node + + if self.tail is None: + # First node in the list + self.tail = node + + def _remove_node(self, node): + """Detach node from the linked list.""" + prev = node.previous + nxt = node.next + + if prev is not None: + prev.next = nxt + else: + # node was head + self.head = nxt + + if nxt is not None: + nxt.previous = prev + else: + # node was tail + self.tail = prev + + node.next = None + node.previous = None + + def _move_to_head(self, node): + """Mark node as most recently used.""" + if node is self.head: + return + self._remove_node(node) + self._add_to_head(node) + + # ---- public API ---- + + def get(self, key): + node = self.map.get(key) + if node is None: + return None + + # mark as recently used + self._move_to_head(node) + return node.value + + def set(self, key, value): + node = self.map.get(key) + + if node is not None: + # update existing + node.value = value + self._move_to_head(node) + return + + # new key + if len(self.map) >= self.limit: + # evict least recently used (tail) + lru = self.tail + if lru is not None: + self._remove_node(lru) + del self.map[lru.key] + + new_node = _Node(key, value) + self._add_to_head(new_node) + self.map[key] = new_node From d49174c0d471b715eead7f4957249c92b22829f2 Mon Sep 17 00:00:00 2001 From: donarablanc Date: Fri, 27 Feb 2026 18:38:04 +0000 Subject: [PATCH 2/3] Update lru_cache.py refactored by following the recommendation to separate DoublyLinkedList from LruCache --- Sprint-2/implement_lru_cache/lru_cache.py | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index b160298..a5bbe48 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -6,17 +6,10 @@ def __init__(self, key, value): self.previous = None -class LruCache: - def __init__(self, limit): - if limit <= 0: - raise ValueError("limit must be positive") - - self.limit = limit - self.map = {} # key -> _Node - self.head = None # most recently used - self.tail = None # least recently used - - # ---- internal helpers for the linked list ---- +class DoublyLinkedList: + def __init__(self): + self.head = None + self.tail = None def _add_to_head(self, node): """Put node at the front (most recently used).""" @@ -59,7 +52,14 @@ def _move_to_head(self, node): self._add_to_head(node) # ---- public API ---- - +class LruCache: + def __init__(self, limit): + if limit <= 0: + raise ValueError("limit must be positive") + self.limit = limit + self.map = {} + self.list = DoublyLinkedList() + def get(self, key): node = self.map.get(key) if node is None: From 3bd33a948c15cffe976113f0f2dd614af548d93b Mon Sep 17 00:00:00 2001 From: donarablanc Date: Sat, 28 Feb 2026 11:35:28 +0000 Subject: [PATCH 3/3] separated and updated lru_cache.py DoublyLinkedList is now only for: adding nodes removing nodes order of nodes LruCache is only checking if: whether a key exists whether cache is full which key to evict --- Sprint-2/implement_lru_cache/lru_cache.py | 84 +++++++++++------------ 1 file changed, 41 insertions(+), 43 deletions(-) diff --git a/Sprint-2/implement_lru_cache/lru_cache.py b/Sprint-2/implement_lru_cache/lru_cache.py index a5bbe48..9750ed6 100644 --- a/Sprint-2/implement_lru_cache/lru_cache.py +++ b/Sprint-2/implement_lru_cache/lru_cache.py @@ -1,91 +1,89 @@ class _Node: - def __init__(self, key, value): - self.key = key - self.value = value + def __init__(self, value): + self.value = value # stores as (key, value) tuple self.next = None self.previous = None class DoublyLinkedList: + """responsible only for managing a doubly linked list. + stores values as (key, value) tuples but knows nothing about cachin + """ def __init__(self): - self.head = None - self.tail = None + self.head = None # most recently used end + self.tail = None # least recently used end - def _add_to_head(self, node): - """Put node at the front (most recently used).""" - node.previous = None + def add_to_head(self, value): + """sreates a new node with value, add to head, returns the node.""" + node = _Node(value) node.next = self.head - + node.previous = None if self.head is not None: self.head.previous = node self.head = node - if self.tail is None: - # First node in the list self.tail = node + return node - def _remove_node(self, node): - """Detach node from the linked list.""" + def remove_node(self, node): + """detaches any node from the list.""" prev = node.previous nxt = node.next - if prev is not None: prev.next = nxt else: - # node was head self.head = nxt - if nxt is not None: nxt.previous = prev else: - # node was tail self.tail = prev - node.next = None node.previous = None - def _move_to_head(self, node): - """Mark node as most recently used.""" - if node is self.head: - return - self._remove_node(node) - self._add_to_head(node) + def remove_tail(self): + """removes and returns the tail node """ + lru = self.tail + if lru is not None: + self.remove_node(lru) + return lru + - # ---- public API ---- class LruCache: + """responsible only for cache logic (get, set, eviction). + gives all all ordering to DoublyLinkedList. + """ def __init__(self, limit): if limit <= 0: raise ValueError("limit must be positive") self.limit = limit - self.map = {} - self.list = DoublyLinkedList() - + self.map = {} + self.list = DoublyLinkedList() + def get(self, key): node = self.map.get(key) if node is None: return None - - # mark as recently used - self._move_to_head(node) - return node.value + # re-inserts at head to mark as most recently used + self.list.remove_node(node) + new_node = self.list.add_to_head(node.value) + self.map[key] = new_node # updates map to point to new node + return new_node.value[1] def set(self, key, value): node = self.map.get(key) - if node is not None: - # update existing - node.value = value - self._move_to_head(node) + # key exists then remove old, adds updated to head + self.list.remove_node(node) + new_node = self.list.add_to_head((key, value)) + self.map[key] = new_node # update map to new node return - - # new key + # new key gets rid of LRU if len(self.map) >= self.limit: - # evict least recently used (tail) - lru = self.tail + lru = self.list.remove_tail() if lru is not None: - self._remove_node(lru) - del self.map[lru.key] - + del self.map[lru.value[0]] + new_node = self.list.add_to_head((key, value)) + self.map[key] = new_node new_node = _Node(key, value) self._add_to_head(new_node) self.map[key] = new_node