Here's most of what you want: a fixed-size least-recently-used cache with O(1) lookup, O(1) insertion, and O(1) deletion It's slightly tricky to get all of these operations to be O(1), hence this slightly elaborate implementation. I combine a hash-table (for fast lookup) with a doubly-linked list of items (for fast removal, re-ordering, and finding the oldest element) (require 'cl) (defstruct lru-cache max-size size newest oldest table) (defstruct lru-item key value next prev) (defun lru-remove-item (item lru) (let ((next (lru-item-next item)) (prev (lru-item-prev item))) (if next (setf (lru-item-prev next) prev) (setf (lru-cache-newest lru) prev)) (if prev (setf (lru-item-next prev) next) (setf (lru-cache-oldest lru) next)))) (defun lru-insert-item (item lru) (let ((newest (lru-cache-newest lru))) (setf (lru-item-next item) nil (lru-item-prev item) newest) (if newest (setf (lru-item-next newest) item) (setf (lru-cache-oldest lru) item)) (setf (lru-cache-newest lru) item))) ;;; Public interface starts here. (defun* lru-create (&key (size 65) (test 'eql)) "Create a new least-recently-used cache and return it.
Takes keyword arguments :SIZE the maximum number of entries (default: 65). :TEST a hash table test (default 'EQL). " (make-lru-cache :max-size size :size 0 :newest nil :oldest nil :table (make-hash-table :size size :test test))) (defun lru-get (key lru &optional default) "Look up KEY in least-recently-used cache LRU and return its associated value.
If KEY is not found, return DEFAULT which defaults to nil. " (let ((item (gethash key (lru-cache-table lru)))) (if item (progn (lru-remove-item item lru) (lru-insert-item item lru) (lru-item-value item)) default))) (defun lru-rem (key lru) "Remove KEY from least-recently-used cache LRU. " (let ((item (gethash key (lru-cache-table lru)))) (when item (remhash (lru-item-key item) (lru-cache-table lru)) (lru-remove-item item lru) (decf (lru-cache-size lru))))) (defun lru-put (key value lru) "Associate KEY with VALUE in least-recently-used cache LRU.
If KEY is already present in LRU, replace its current value with VALUE. " (let ((item (gethash key (lru-cache-table lru)))) (if item (setf (lru-item-value item) value) (when (eql (lru-cache-size lru) (lru-cache-max-size lru)) (lru-rem (lru-item-key (lru-cache-oldest lru)) lru)) (let ((newitem (make-lru-item :key key :value value))) (lru-insert-item newitem lru) (puthash key newitem (lru-cache-table lru)) (incf (lru-cache-size lru)))))) ;;; Exercise for the reader: implement lru-clr and lru-map to complete the ;;; analogy with hash tables For your application that's keyed on pairs, you probably want to supply :test 'equal to lru-create Or see Defining Hash Comparisons if you need something special I'll let you figure out how to do the time-based expiry; it should be straightforward from here (If anyone knows a simpler way to implement this while keeping the operations running in constant time, I'd be very interested to see it. ).
Here's most of what you want: a fixed-size least-recently-used cache with O(1) lookup, O(1) insertion, and O(1) deletion. It's slightly tricky to get all of these operations to be O(1), hence this slightly elaborate implementation. I combine a hash-table (for fast lookup) with a doubly-linked list of items (for fast removal, re-ordering, and finding the oldest element).(require 'cl) (defstruct lru-cache max-size size newest oldest table) (defstruct lru-item key value next prev) (defun lru-remove-item (item lru) (let ((next (lru-item-next item)) (prev (lru-item-prev item))) (if next (setf (lru-item-prev next) prev) (setf (lru-cache-newest lru) prev)) (if prev (setf (lru-item-next prev) next) (setf (lru-cache-oldest lru) next)))) (defun lru-insert-item (item lru) (let ((newest (lru-cache-newest lru))) (setf (lru-item-next item) nil (lru-item-prev item) newest) (if newest (setf (lru-item-next newest) item) (setf (lru-cache-oldest lru) item)) (setf (lru-cache-newest lru) item))) ;;; Public interface starts here.(defun* lru-create (&key (size 65) (test 'eql)) "Create a new least-recently-used cache and return it.
Takes keyword arguments :SIZE the maximum number of entries (default: 65). :TEST a hash table test (default 'EQL). " (make-lru-cache :max-size size :size 0 :newest nil :oldest nil :table (make-hash-table :size size :test test))) (defun lru-get (key lru &optional default) "Look up KEY in least-recently-used cache LRU and return its associated value.
If KEY is not found, return DEFAULT which defaults to nil. " (let ((item (gethash key (lru-cache-table lru)))) (if item (progn (lru-remove-item item lru) (lru-insert-item item lru) (lru-item-value item)) default))) (defun lru-rem (key lru) "Remove KEY from least-recently-used cache LRU. " (let ((item (gethash key (lru-cache-table lru)))) (when item (remhash (lru-item-key item) (lru-cache-table lru)) (lru-remove-item item lru) (decf (lru-cache-size lru))))) (defun lru-put (key value lru) "Associate KEY with VALUE in least-recently-used cache LRU.
If KEY is already present in LRU, replace its current value with VALUE. " (let ((item (gethash key (lru-cache-table lru)))) (if item (setf (lru-item-value item) value) (when (eql (lru-cache-size lru) (lru-cache-max-size lru)) (lru-rem (lru-item-key (lru-cache-oldest lru)) lru)) (let ((newitem (make-lru-item :key key :value value))) (lru-insert-item newitem lru) (puthash key newitem (lru-cache-table lru)) (incf (lru-cache-size lru)))))) ;;; Exercise for the reader: implement lru-clr and lru-map to complete the ;;; analogy with hash tables. For your application that's keyed on pairs, you probably want to supply :test 'equal to lru-create.
Or see Defining Hash Comparisons if you need something special. I'll let you figure out how to do the time-based expiry; it should be straightforward from here.(If anyone knows a simpler way to implement this while keeping the operations running in constant time, I'd be very interested to see it. ).
Here's most of what you want: a fixed-size least-recently-used cache with O(1) lookup, O(1) insertion, and O(1) deletion. It's slightly tricky to get all of these operations to be O(1), hence this slightly elaborate implementation. I combine a hash-table (for fast lookup) with a doubly-linked list of items (for fast removal, re-ordering, and finding the oldest element).
For your application that's keyed on pairs, you probably want to supply :test 'equal to lru-create. Or see Defining Hash Comparisons if you need something special. I'll let you figure out how to do the time-based expiry; it should be straightforward from here.
I cant really gove you an answer,but what I can give you is a way to a solution, that is you have to find the anglde that you relate to or peaks your interest. A good paper is one that people get drawn into because it reaches them ln some way.As for me WW11 to me, I think of the holocaust and the effect it had on the survivors, their families and those who stood by and did nothing until it was too late.