Initial import
This commit is contained in:
195
scribblings/lru-cache.scrbl
Normal file
195
scribblings/lru-cache.scrbl
Normal file
@@ -0,0 +1,195 @@
|
||||
#lang scribble/manual
|
||||
@(require scribble/example)
|
||||
|
||||
@title{LRU Cache (Least Recently Used) for Racket}
|
||||
@author{You}
|
||||
|
||||
@defmodule[lru-cache]{
|
||||
An in-memory LRU cache with optional item expiration. The cache keeps items in
|
||||
recency order and evicts the least recently used item when the maximum capacity
|
||||
is reached. Optionally, items can expire after a given number of seconds.
|
||||
}
|
||||
|
||||
@section{Overview}
|
||||
|
||||
This module provides:
|
||||
|
||||
@itemlist[
|
||||
@item{@racket[make-lru] to construct a cache with a @italic{max-count}, an
|
||||
optional equality/compare function for items, and optional expiration (seconds).}
|
||||
@item{Mutation operations: @racket[lru-add!], @racket[lru-clear],
|
||||
@racket[set-lru-max-count!], @racket[set-lru-expire!].}
|
||||
@item{Query operations: @racket[lru-has?], @racket[lru-count], @racket[lru->list],
|
||||
@racket[lru-expires?], @racket[lru-expire], @racket[lru-max-count], @racket[lru?].}
|
||||
]
|
||||
|
||||
The cache stores @emph{items} (values) and uses a comparison function to decide
|
||||
whether an item is already present. Each item is associated with a last-access
|
||||
timestamp that drives recency; on insert or hit, the item is bubbled to the front.
|
||||
|
||||
@bold{Thread-safety:} The implementation uses a @racket[box] internally and is
|
||||
@emph{not} intrinsically thread-safe. Use external synchronization for concurrent access.
|
||||
|
||||
@section{Data Types}
|
||||
|
||||
@defstruct*[lru* ([cache box?]
|
||||
[compare procedure?]
|
||||
[max-count (and/c integer? (>/c 0))]
|
||||
[expire-in-seconds (or/c #f (and/c integer? (>/c 0)))])]{
|
||||
Internal representation of the LRU cache. Use @racket[lru?] to test for this type;
|
||||
do not rely on fields directly. Create instances with @racket[make-lru].
|
||||
}
|
||||
|
||||
@defproc[(lru? [v any/c]) boolean?]{
|
||||
Predicate that returns @racket[#t] when @racket[v] is an LRU instance.
|
||||
}
|
||||
|
||||
@section{Construction}
|
||||
|
||||
@defproc[(make-lru
|
||||
[max-count (and/c integer? (>/c 0))]
|
||||
[#:cmp compare (-> any/c any/c boolean?) equal?]
|
||||
[#:expire expire-in-seconds (or/c #f (and/c integer? (>/c 0))) #f])
|
||||
lru?]{
|
||||
Create a new LRU cache.
|
||||
|
||||
@itemlist[
|
||||
@item{@racket[max-count] — Maximum number of items kept in the cache. When
|
||||
the limit is exceeded, the least recently used item is evicted.}
|
||||
@item{@racket[#:cmp compare] — Binary comparison function that returns
|
||||
@racket[#t] when two items are considered equal (default: @racket[equal?]).}
|
||||
@item{@racket[#:expire expire-in-seconds] — If a positive integer: items expire
|
||||
automatically after that many seconds of inactivity. @racket[#f] disables
|
||||
expiration (default).}
|
||||
]
|
||||
|
||||
Returns A new empty LRU cache with the given parameters.
|
||||
}
|
||||
|
||||
@section{Core Operations}
|
||||
|
||||
@defproc[(lru-add! [l lru?] [item any/c]) lru?]{
|
||||
Insert @racket[item]. If it already exists (per @racket[compare]), it is
|
||||
considered a hit: moved to the front and its access time is refreshed.
|
||||
If capacity is reached, the least recently used item is removed.
|
||||
Returns @racket[l].
|
||||
}
|
||||
|
||||
@defproc[(lru-has? [l lru?] [item any/c]) boolean?]{
|
||||
Returns @racket[#t] if @racket[item] is currently present (and not expired),
|
||||
otherwise @racket[#f]. This call also performs lazy cleanup of expired items.
|
||||
}
|
||||
|
||||
@defproc[(lru-clear [l lru?]) lru?]{
|
||||
Clear all items from the cache. Returns @racket[l].
|
||||
}
|
||||
|
||||
@defproc[(lru-count [l lru?]) (and/c integer? (>=/c 0))]{
|
||||
Return the number of @emph{non-expired} items currently in the cache.
|
||||
}
|
||||
|
||||
@defproc[(lru->list [l lru?] [#:with-expire with-expire? boolean? #f]) list?]{
|
||||
Return the cache contents in recency order (most recent first).
|
||||
When @racket[with-expire?] is @racket[#f] (default), returns just the list of items.
|
||||
When @racket[with-expire?] is @racket[#t], returns @racket[(list item age-in-seconds)]
|
||||
pairs, where @racket[age-in-seconds] is the elapsed time since last access/insert.
|
||||
}
|
||||
|
||||
@section{Parameters and Configuration}
|
||||
|
||||
@defproc[(lru-max-count [l lru?]) (and/c integer? (>/c 0))]{
|
||||
Read the maximum capacity.
|
||||
}
|
||||
|
||||
@defproc[(set-lru-max-count! [l lru?] [n (and/c integer? (>/c 0))]) lru?]{
|
||||
Set the maximum capacity. If the current size exceeds @racket[n], trimming
|
||||
will occur on subsequent mutations or cleanups, depending on usage. Returns @racket[l].
|
||||
}
|
||||
|
||||
@defproc[(lru-expire [l lru?]) (or/c #f (and/c integer? (>/c 0)))]{
|
||||
Read the expiration period in seconds, or @racket[#f] if expiration is disabled.
|
||||
}
|
||||
|
||||
@defproc[(set-lru-expire! [l lru?] [expire (or/c #f (and/c integer? (>/c 0)))]) lru?]{
|
||||
Set the expiration period (seconds) or disable it with @racket[#f]. Returns @racket[l].
|
||||
}
|
||||
|
||||
@defproc[(lru-expires? [l lru?]) boolean?]{
|
||||
Return @racket[#t] when expiration is enabled (i.e., @racket[(lru-expire l)] is
|
||||
a positive integer), otherwise @racket[#f].
|
||||
}
|
||||
|
||||
@section{Examples}
|
||||
|
||||
@examples[
|
||||
#:eval (make-base-eval)
|
||||
(require racket/base)
|
||||
|
||||
;; Assuming your module is "lru.rkt" next to this file:
|
||||
(require "../private/lru-cache.rkt")
|
||||
|
||||
(define L (make-lru 3)) ;; max 3 items, no expiration
|
||||
(lru-add! L 'a)
|
||||
(lru-add! L 'b)
|
||||
(lru-add! L 'c)
|
||||
(lru->list L) ;; '(c b a)
|
||||
|
||||
(lru-add! L 'b) ;; hit: bubble to front
|
||||
(lru->list L) ;; '(b c a)
|
||||
|
||||
(lru-add! L 'd) ;; capacity 3 -> evicts LRU ('a)
|
||||
(lru->list L) ;; '(d b c)
|
||||
(lru-count L) ;; 3
|
||||
|
||||
(lru-has? L 'c) ;; #t
|
||||
(lru-has? L 'a) ;; #f
|
||||
|
||||
(set-lru-expire! L 1) ;; items expire after 1 second
|
||||
(sleep 2)
|
||||
(lru-has? L 'b) ;; #f (expired)
|
||||
(lru->list L) ;; '()
|
||||
|
||||
(lru-add! L 'x)
|
||||
(lru->list L #:with-expire #t) ;; '((x 0)) -- age ~ 0 sec
|
||||
|
||||
(set-lru-max-count! L 1)
|
||||
(lru-add! L 'y)
|
||||
(lru->list L) ;; '(y)
|
||||
(lru-clear L)
|
||||
(lru->list L) ;; '()
|
||||
]
|
||||
|
||||
@section{Behavior Details}
|
||||
|
||||
@subsection{Recency and Hits}
|
||||
Any insertion or hit refreshes the access time and moves the item to the front.
|
||||
When capacity is reached, the least recently used item is evicted.
|
||||
|
||||
@subsection{Expiration}
|
||||
If @racket[lru-expire] is a positive integer, items become expired when their
|
||||
last access is older than that threshold. Cleanup is performed lazily on calls
|
||||
such as @racket[lru-has?], @racket[lru-count], and @racket[lru->list].
|
||||
|
||||
@subsection{Comparison Function}
|
||||
The @racket[#:cmp] function decides item equality (e.g., @racket[equal?],
|
||||
@racket[=] for numbers, or a custom predicate). For predictable behavior, it
|
||||
should be symmetric and transitive.
|
||||
|
||||
@section{Errors and Contracts}
|
||||
Contracts enforce:
|
||||
@itemlist[
|
||||
@item{@racket[max-count], @racket[lru-max-count], and @racket[set-lru-max-count!]
|
||||
use positive integers.}
|
||||
@item{@racket[#:expire] and @racket[set-lru-expire!] accept @racket[#f] or
|
||||
positive integers.}
|
||||
@item{@racket[#:cmp] is a two-argument function returning a @racket[boolean?].}
|
||||
]
|
||||
Invalid inputs raise contract violations with descriptive error messages.
|
||||
|
||||
@section{Notes}
|
||||
@itemlist[
|
||||
@item{The implementation is not thread-safe; use locks for concurrent access.}
|
||||
@item{Expiration cleanup is lazy. If you need stricter guarantees, trigger
|
||||
periodic queries (e.g., @racket[lru-count]) or add an explicit cleanup function.}
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user