LRU caches

To use the bindings from this module:

(import :std/misc/lru)

make-lru-cache

(make-lru-cache cap) -> lru-cache | error

  cap := max capacity of cache, fixnum

Creates a new empty Least Recently Used (LRU) cache, a cache replacement data structure, which tracks element usage and drops seldom used ones when full to make room for new elements. cap is the capacity, i.e., the number of elements the cache can hold before purging older entries. cap needs to be at least 1, otherwise an error is signaled.

Examples:

> (def lru (make-lru-cache 3))
> (lru-cache-put! lru 'a "heavy-load-01")
> (lru-cache-put! lru 'b "heavy-load-02")
> (lru-cache-put! lru 'c "heavy-load-03")
> (lru-cache-put! lru 'd "heavy-load-04")
> (lru-cache-put! lru 'e "heavy-load-05")
> (lru-cache-size lru)
3                  ; cache full, older entries are eviced first:
> (lru-cache->list lru)
((e . "heavy-load-05") (d . "heavy-load-04") (c . "heavy-load-03"))
> (lru-cache-get lru 'c)
"heavy-load-03"    ; cache hit, bring to front:
> (lru-cache->list lru)
((c . "heavy-load-03") (e . "heavy-load-05") (d . "heavy-load-04"))

> (import :std/iter)
> (for ((values key load) lru)
    (displayln "key: " key ", load: " load))
key: c, load: heavy-load-03
key: e, load: heavy-load-05
key: d, load: heavy-load-04

lru-cache?

(lru-cache? lru) -> boolean

  lru := lru-cache to check

Returns #t if lru is an LRU cache, #f otherwise.

Examples:

> (lru-cache? (make-lru-cache 25))
#t

lru-cache-size

(lru-cache-size lru) -> fixnum

  lru := lru-cache to check

Returns the current size (i.e., the number of elements the LRU cache holds, not the capacity) of lru.

Examples:

> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 1 #x01)
    (lru-cache-put! lru 2 #x02)
    (lru-cache-size lru))
2

> (lru-cache-size (make-lru-cache 1000))
0

lru-cache-capacity

(lru-cache-capacity lru) -> fixnum

  lru := lru-cache to check

Returns the maximum number of elements lru can hold.

Examples:

> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 1 #x01)
    (lru-cache-put! lru 2 #x02)
    (lru-cache-capacity lru))
3

> (lru-cache-capacity (make-lru-cache 1000))
1000

lru-cache-put!

(lru-cache-put! lru key val) -> void

  lru      := lru-cache
  key, val := key-value association to insert into lru

Puts an association of key to val into lru, in the queue head position to be precise. If the cache is full, then the tail of the LRU queue (i.e., the value least recently used) is dropped from the cache.

Examples:

> (defstruct resource (raw-data))
> (make-lru-cache 3)
#<lru-cache #35>
> (lru-cache-put! #35 1 (make-resource 'HUGE))
> (lru-cache-put! #35 2 (make-resource 'SMALL))
> (lru-cache-put! #35 3 (make-resource 'LARGE))
> (lru-cache->list #35)
((3 . #<resource #36>) (2 . #<resource #37>) (1 . #<resource #38>))
> (lru-cache-put! #35 4 'non-resource)    ; cache is full, evict old element
> (lru-cache->list #35)
((4 . non-resource) (3 . #<resource #36>) (2 . #<resource #37>))
> (resource-raw-data #38)
HUGE

lru-cache-remove!

(lru-cache-remove! lru key) -> void

  lru := lru-cache to remove from
  key := key to look up

Removes the association of key from lru, making room for a new element in the lru cache.

Examples:

> (make-lru-cache 3)
#<lru-cache #39>
> (lru-cache-put! #39 1 "this")
> (lru-cache-put! #39 1 "that")    ; same key, simply updated
> (lru-cache->list #39)
((1 . "that"))
> (lru-cache-remove! #39 1)
> (lru-cache->list #39)
()

lru-cache-ref

(lru-cache-ref lru key [default = absent-obj]) -> any | default | error

  lru     := lru-cache to access
  key     := key to look up
  default := optional element returned when key can't be found

Returns the association of key in lru, and promotes the node to the head of the LRU queue. If there is no association, then default is returned. If the default is omitted, then an error is raised.

Examples:

> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 'a "file-a")
    (lru-cache-put! lru 'b "file-b")
    (lru-cache-put! lru 'c "file-c")
    (displayln (lru-cache->list lru))
    (displayln (lru-cache-ref lru 'b 'NONE))
    (displayln (lru-cache->list lru))
    (lru-cache-ref lru 'd 'NONE))
((c . file-c) (b . file-b) (a . file-a))
file-b
((b . file-b) (c . file-c) (a . file-a))
NONE

lru-cache-get

(lru-cache-ref lru key) -> any | #f

  lru := lru-cache to access
  key := key to look up

Same as (lru-cache-ref lru key #f).

Examples:

> (lru-cache-get (make-lru-cache 3) 'not-in-here)
#f

lru-cache-flush!

(lru-cache-flush! lru) -> lru-cache

  lru := lru-cache to clear

Removes all elements from lru and returns the empty LRU cache. The capacity remains unchanged.

Examples:

> (let (lru (make-lru-cache 100))
    (lru-cache-put! lru "first"  '1st)
    (lru-cache-put! lru "second" '2nd)
    (lru-cache-put! lru "third"  '3rd)
    (displayln (lru-cache-size lru) " " (lru-cache-capacity lru))
    (displayln (lru-cache-flush! lru))
    (displayln (lru-cache-size lru) " " (lru-cache-capacity lru)))
3 100
#<lru-cache #12>
0 100

lru-cache-for-each

(lru-cache-for-each proc lru) -> void

  proc := procedure to be called for each key-value pair in lru
  lru  := lru-cache

Applies (proc key val) for every key-value association in lru, in Most Recently Used (MRU) order. Isn't returning anything, executed for its side effects.

Examples:

> (make-lru-cache 3)
#<lru-cache #43>
> (for-each (lambda (x) (lru-cache-put! #43 x (* x x))) [1 2 3 4 5])
> (lru-cache-for-each (lambda (k v) (displayln k " " v)) #43)
5 25
4 16
3 9

lru-cache-walk

(lru-cache-walk proc lru) -> void

  proc := procedure to be called for each key-value pair in lru
  lru  := lru cache

Same as (lru-cache-for-each proc lru).

lru-cache-fold

(lru-cache-fold proc init lru) -> any

  proc := procedure to be called for each key-value pair in lru
  init := initial value
  lru  := lru-cache

Folds lru in Most Recently Used (MRU) order.

proc's signature is expected to look like this: (proc key val prev-intermediate) -> next-intermediate). lru-cache-fold returns the last next-intermediate value produced by proc. Furthermore, prev-intermediate will be set to init at the beginning.

Examples:

> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 'a "creatures")
    (lru-cache-put! lru 'b "fluffy")
    (lru-cache-put! lru 'c "are")
    (lru-cache-fold (lambda (k v i) (string-append i " " v))
                    "gerbils" lru))
"gerbils are fluffy creatures"

lru-cache-foldr

(lru-cache-foldr proc init lru) -> any

  proc := procedure to be called for each key-value pair in lru
  init := initial value
  lru  := lru-cache

Where lru-cache-fold folds in Most Recently Used (MRU) order, lru-cache-foldr folds lru in Least Recently Used (LRU) order.

Examples:

> (let (lru (make-lru-cache 5))
    (lru-cache-put! lru 'a (iota 5))
    (lru-cache-put! lru 'b (iota 4))
    (lru-cache-put! lru 'c (iota 3))
    (lru-cache-fold (lambda (k v i) (cons v i)) [] lru))
((0 1 2 3 4) (0 1 2 3) (0 1 2))

lru-cache->list

(lru-cache->list lru) -> alist

  lru := lru-cache

Returns an alist of key-value associations in lru, in Most Recently Used (MRU) order.

Examples:

> (import :std/srfi/14)
> (make-lru-cache 10)
#<lru-cache #47>
> (for-each (cut lru-cache-put! #47 <> <>) (iota 10) (char-set->list char-set:letter))
> (lru-cache->list #47)
((9 . #\J)
 (8 . #\I)
 (7 . #\H)
 (6 . #\G)
 (5 . #\F)
 (4 . #\E)
 (3 . #\D)
 (2 . #\C)
 (1 . #\B)
 (0 . #\A))

in-lru-cache

(in-lru-cache lru) -> iterator

  lru := lru-cache to iterate over

Creates an iterator over lru, yielding key-value values in Most Recently Used (MRU) order.

Examples:

> (import :std/iter)
> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 1 #\a)
    (lru-cache-put! lru 2 #\b)
    (lru-cache-put! lru 3 #\c)
    (for ((values k v) (in-lru-cache lru))    ; or short: (for ((values k v) lru) ...)
      (displayln k " -> " v)))
3 -> c
2 -> b
1 -> a

in-lru-cache-keys

(in-lru-cache-keys lru) -> iterator

  lru := lru-cache to iterate over

Creates an iterator over lru, yielding keys in Most Recently Used (MRU) order.

Examples:

> (import :std/iter)
> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 1 #\a)
    (lru-cache-put! lru 2 #\b)
    (lru-cache-put! lru 3 #\c)
    (for (x (in-lru-cache-keys lru))
      (displayln x)))
3
2
1

in-lru-cache-values

(in-lru-cache-values lru) -> iterator

  lru := lru-cache to iterate over

Creates an iterator over lru, yielding values in Most Recently Used (MRU) order.

Examples:

> (import :std/iter)
> (let (lru (make-lru-cache 3))
    (lru-cache-put! lru 1 #\a)
    (lru-cache-put! lru 2 #\b)
    (lru-cache-put! lru 3 #\c)
    (for (x (in-lru-cache-values lru))
      (displayln x)))
c
b
a

lru-cache

(defsyntax lru-cache)

LRU cache type for user-defined generics and destructuring.

Examples:

;; possible lru-cache iterator implementation:
(defmethod (:iter (lru lru-cache))
  (in-lru-cache lru))

(def (in-lru-cache lru)
  (def (iterate)
    (lru-cache-for-each yield lru))
  (in-coroutine iterate))