== lru-cache
[[toc:]]
=== Description
An LRU (least recently used) cache for Chicken Scheme 5.
Keys and values may be of any type. The cache uses a hash table for O(1)
lookup and a doubly-linked list to maintain access ordering. When the
cache reaches capacity, the least recently used entry is evicted.
This implementation shares no lineage with the
[[/eggref/4/lru-cache|lru-cache egg]], for Chicken Scheme 4, by Jim
Ursetto.
=== Author
Christopher Harrison ([[https://tweag.io|Tweag]])
=== Repository
[[https://github.com/tweag/lru-cache]]
=== Requirements
* [[/egg/srfi-69|srfi-69]]
* [[/egg/matchable|matchable]]
=== API
==== Cache creation
(make-lru-cache [capacity])
Create a new LRU cache. {{capacity}} is the maximum number of entries
the cache will hold before evicting the least recently used entry;
defaulting to 64.
==== Lookup and mutation
(lru-cache-ref cache key)
Returns the value associated with {{key}} in {{cache}}, promoting it to
the most recently used position. Signals an error if {{key}} is not
present.
(lru-cache-ref cache key thunk)
Returns the value associated with {{key}} in {{cache}} if present,
promoting it to the most recently used position. If {{key}} is not
present, calls {{thunk}} (a procedure of zero arguments) to compute the
value, caches the result and returns it.
(lru-cache-set! cache key value)
Associates {{key}} with {{value}} in {{cache}}. If {{key}} already
exists, updates its value and promotes it to the most recently used
position. If {{key}} is new and the cache is at capacity, the least
recently used entry is evicted.
(lru-cache-delete! cache key)
Removes the entry for {{key}} from {{cache}}. Signals an error if
{{key}} is not present.
(lru-cache-clear! cache)
Removes all entries from {{cache}}.
==== Inspection
(lru-cache-size cache)
Returns the number of entries currently in {{cache}}.
(lru-cache-capacity cache)
Returns the maximum number of entries {{cache}} can hold.
(lru-cache-has-key? cache key)
Returns {{#t}} if {{key}} is present in {{cache}}, {{#f}} otherwise.
Does not affect the access ordering.
==== Iteration
(lru-cache-for-each cache proc)
Apply {{proc}} (a procedure of two arguments: the entry key and value,
respectively) to each entry in {{cache}}, ordered from most recently
used to least recently used. Does not affect the access ordering.
'''Note:''' {{proc}} must not mutate {{cache}}.
(lru-cache-fold cache proc init)
Calls {{proc}} (a procedure of three arguments: the entry key, value and
accumulator, respectively) with each entry in {{cache}}, ordered from
most recently to least recently used; the initial folded value is
{{init}}, returns the final folded value. Does not affect the access
ordering.
'''Note:''' {{proc}} must not mutate {{cache}}.
(lru-cache->alist cache)
Returns an association list of all key-value pairs in {{cache}}, ordered
from most recently used to least recently used. Does not affect the
access ordering.
(lru-cache-keys cache)
Returns a list of all keys in {{cache}}, ordered from most recently used
to least recently used. Does not affect the access ordering.
(lru-cache-values cache)
Returns a list of all values in {{cache}}, ordered from most recently
used to least recently used. Does not affect the access ordering.
==== Memoisation
(define-memoised/lru [capacity] (name arg ...) body ...)
Defines a top-level procedure {{name}}, with its argument list and
body definition per usual, but return values are cached based on the
arguments (compared as a list). {{capacity}} defaults to 64.
(memoise/lru proc [capacity])
Returns a new procedure that caches the results of calling {{proc}}.
Arguments are used as the cache key (compared as a list). {{capacity}}
defaults to 64.
'''Note:''' For recursive procedures, the memoised version must replace
the original binding for recursive calls to benefit from caching. It is
better to use {{define-memoised/lru}} in such cases, wherever possible.
(define (fib n)
(cond
((= n 0) 1)
((= n 1) 1)
(else (+ (fib (- n 1)) (fib (- n 2))))))
(set! fib (memoise/lru fib))
=== Examples
(import lru-cache
(chicken format))
;; Create a cache with capacity 3
(define cache (make-lru-cache 3))
;; Add some entries
(lru-cache-set! cache 'a 1)
(lru-cache-set! cache 'b 2)
(lru-cache-set! cache 'c 3)
(lru-cache-keys cache) ; => (c b a)
;; Accessing an entry promotes it
(lru-cache-ref cache 'a) ; => 1
(lru-cache-keys cache) ; => (a c b)
;; Adding a fourth entry evicts the LRU
(lru-cache-set! cache 'd 4)
(lru-cache-keys cache) ; => (d a c)
(lru-cache-has-key? cache 'b) ; => #f
;; Using a thunk for cache-or-compute
(lru-cache-ref cache 'e
(lambda () (+ 40 2))) ; => 42
;; Memoisation
(define-memoised/lru (fib n)
(printf "computing fib ~A~N" n)
(cond
((= n 0) 1)
((= n 1) 1)
(else (+ (fib (- n 1)) (fib (- n 2))))))
(fib 5) ; prints "computing fib 5" .. "computing fib 0", returns 8
(fib 5) ; returns 8, no printing
=== License
LGPL-3.0-or-later
=== Version history
; 0.1.0 : Initial release