epsilon.list¶
List operations for association lists, property lists, and circular lists.
Overview¶
The epsilon.list
package provides list manipulation functions for association lists, property lists, circular lists, and list modification macros.
Quick Start¶
(defpackage :my-app
(:use :cl)
(:local-nicknames (:lst :epsilon.list)))
(in-package :my-app)
;; Property list operations
(lst:plist-alist '(:a 1 :b 2)) ; => ((:A . 1) (:B . 2))
(lst:alist-plist '((:a . 1))) ; => (:A 1)
;; Circular lists
(defparameter *circle* (lst:circular-list 1 2 3))
(lst:circular-list-p *circle*) ; => T
;; List utilities
(lst:ensure-list 42) ; => (42)
(lst:ensure-list '(1 2)) ; => (1 2)
Function Reference¶
Association List Operations¶
alist-plist¶
(alist-plist alist) => plist
Convert association list to property list.
(alist-plist '((:name . "Alice") (:age . 30)))
; => (:NAME "Alice" :AGE 30)
plist-alist¶
(plist-alist plist) => alist
Convert property list to association list.
(plist-alist '(:name "Alice" :age 30))
; => ((:NAME . "Alice") (:AGE . 30))
assoc-value¶
(assoc-value alist key &key (test 'eql)) => value, entry
SETF-able accessor for association list values.
(defparameter *alist* '((:a . 1) (:b . 2)))
(assoc-value *alist* :a) ; => 1, (:A . 1)
(setf (assoc-value *alist* :c) 3) ; Adds (:c . 3)
rassoc-value¶
(rassoc-value alist value &key (test 'eql)) => key, entry
SETF-able reverse association list accessor.
(rassoc-value '((1 . :one) (2 . :two)) :one)
; => 1, (1 . :ONE)
racons¶
(racons key value ralist) => alist
Reverse acons - add (VALUE . KEY) to alist.
(racons :name "John" '()) ; => (("John" . :NAME))
Property List Operations¶
doplist¶
(doplist (key val plist &optional values) &body body)
Iterate over property list key-value pairs.
(doplist (k v '(:a 1 :b 2 :c 3))
(format t "~A = ~A~%" k v))
; Prints:
; A = 1
; B = 2
; C = 3
remove-from-plist¶
(remove-from-plist plist &rest keys) => plist
Return plist without specified keys (non-destructive).
(remove-from-plist '(:a 1 :b 2 :c 3) :b)
; => (:A 1 :C 3)
delete-from-plist¶
(delete-from-plist plist &rest keys) => plist
Remove keys from plist (potentially destructive).
(delete-from-plist '(:a 1 :b 2 :c 3) :a :c)
; => (:B 2)
remove-from-plistf¶
(remove-from-plistf place &rest keys)
Modify macro that removes keys from plist at place.
(defparameter *plist* '(:a 1 :b 2 :c 3))
(remove-from-plistf *plist* :b)
*plist* ; => (:A 1 :C 3)
delete-from-plistf¶
(delete-from-plistf place &rest keys)
Modify macro that destructively removes keys from plist.
Circular List Operations¶
circular-list¶
(circular-list &rest elements) => circular-list
Create a circular list from elements.
(circular-list 1 2 3) ; => circular list #1=(1 2 3 . #1#)
make-circular-list¶
(make-circular-list &rest elements) => circular-list
Create a circular list (alternative constructor).
(make-circular-list 'a 'b) ; => circular list
circular-list-p¶
(circular-list-p object) => boolean
Return T if object is a circular list.
(circular-list-p (circular-list 1 2)) ; => T
(circular-list-p '(1 2 3)) ; => NIL
circular-tree-p¶
(circular-tree-p object) => boolean
Return T if object contains circular references in its tree structure.
(circular-tree-p '(1 2 . #1=(3 . #1#))) ; => T
circular-list-subseq¶
(circular-list-subseq list start end) => list
Extract a subsequence from a circular list.
(circular-list-subseq (circular-list 1 2 3) 0 5)
; => (1 2 3 1 2)
List Type Checking¶
proper-list-p¶
(proper-list-p object) => boolean
Return T if object is a proper list (NIL-terminated, non-circular).
(proper-list-p '(1 2 3)) ; => T
(proper-list-p '(1 . 2)) ; => NIL
(proper-list-p (circular-list 1)) ; => NIL
proper-list-length¶
(proper-list-length list) => integer
Return length of list, signaling error if not proper.
(proper-list-length '(a b c)) ; => 3
(proper-list-length '(1 . 2)) ; Signals error
safe-endp¶
(safe-endp x) => boolean
Safe version of ENDP that checks proper list termination.
(safe-endp '()) ; => T
(safe-endp '(1)) ; => NIL
setp¶
(setp object &key (test 'eql)) => boolean
Test if object is a list with no duplicate elements.
(setp '(1 2 3)) ; => T
(setp '(1 2 1)) ; => NIL
set-equal¶
(set-equal list1 list2 &key (test 'eql)) => boolean
Test if two lists contain the same elements (as sets).
(set-equal '(1 2 3) '(3 2 1)) ; => T
(set-equal '(a b) '(a b c)) ; => NIL
List Utilities¶
ensure-list¶
(ensure-list list) => list
If list is already a list, return it. Otherwise wrap in a list.
(ensure-list 42) ; => (42)
(ensure-list '(1 2)) ; => (1 2)
(ensure-list nil) ; => NIL
ensure-cons¶
(ensure-cons cons) => cons
If cons is a cons, return it. Otherwise create (cons . nil).
(ensure-cons '(1 . 2)) ; => (1 . 2)
(ensure-cons 42) ; => (42 . NIL)
ensure-car¶
(ensure-car thing) => value
If thing is a cons, return its car. Otherwise return thing.
(ensure-car '(1 2 3)) ; => 1
(ensure-car 42) ; => 42
lastcar¶
(lastcar list) => value
Return the last element of a proper list.
(lastcar '(1 2 3)) ; => 3
(lastcar '(a)) ; => A
flatten¶
(flatten list) => list
Flatten nested list structure into a single list.
(flatten '((1 2) (3 (4 5)))) ; => (1 2 3 4 5)
(flatten '(a (b) ((c)))) ; => (A B C)
mappend¶
(mappend function list) => list
Map function over list and append results.
(mappend (lambda (x) (list x (* x 2)))
'(1 2 3))
; => (1 2 2 4 3 6)
sans¶
(sans plist &rest keys) => plist
Remove keys from property list (non-destructive).
(sans '(:a 1 :b 2 :c 3) :b :c) ; => (:A 1)
Modify Macros¶
appendf¶
(appendf place &rest lists)
Modify macro for APPEND.
(defparameter *list* '(1 2))
(appendf *list* '(3 4) '(5))
*list* ; => (1 2 3 4 5)
nconcf¶
(nconcf place &rest lists)
Modify macro for NCONC (destructive concatenation).
(defparameter *list* (list 1 2))
(nconcf *list* (list 3 4))
*list* ; => (1 2 3 4)
unionf¶
(unionf place list &rest args)
Modify macro for UNION.
(defparameter *set* '(1 2 3))
(unionf *set* '(3 4 5))
*set* ; => (1 2 3 4 5)
nunionf¶
(nunionf place list &rest args)
Modify macro for NUNION (destructive union).
(defparameter *set* (list 1 2))
(nunionf *set* (list 2 3))
*set* ; => (1 2 3)
reversef¶
(reversef place)
Modify macro for REVERSE.
(defparameter *list* '(1 2 3))
(reversef *list*)
*list* ; => (3 2 1)
nreversef¶
(nreversef place)
Modify macro for NREVERSE (destructive reverse).
(defparameter *list* (list 1 2 3))
(nreversef *list*)
*list* ; => (3 2 1)
Error Handling¶
malformed-plist¶
(malformed-plist plist) => error
Signal an error for a malformed property list.
(malformed-plist '(:a 1 :b)) ; Signals error
circular-list-error¶
(circular-list-error list) => error
Signal a TYPE-ERROR for a circular list.
Type Definitions¶
proper-list¶
Type designator for proper lists.
(typep '(1 2 3) 'proper-list) ; => T
(typep '(1 . 2) 'proper-list) ; => NIL
circular-list¶
Type designator for circular lists.
(typep (circular-list 1) 'circular-list) ; => T
Examples¶
Working with Configuration¶
;; Convert between alist and plist formats
(defparameter *config-alist*
'((:host . "localhost")
(:port . 8080)
(:debug . t)))
(defparameter *config-plist*
(alist-plist *config-alist*))
; => (:HOST "localhost" :PORT 8080 :DEBUG T)
;; Remove sensitive keys
(remove-from-plist *config-plist* :debug)
; => (:HOST "localhost" :PORT 8080)
Circular List for Round-Robin¶
(defparameter *servers*
(circular-list "server1" "server2" "server3"))
(defun get-next-server ()
(prog1 (car *servers*)
(setf *servers* (cdr *servers*))))
(get-next-server) ; => "server1"
(get-next-server) ; => "server2"
(get-next-server) ; => "server3"
(get-next-server) ; => "server1" (cycles back)
Property List Iteration¶
(defparameter *props* '(:width 100 :height 200 :color "blue"))
(doplist (key value *props*)
(format t "~A: ~A~%" key value))
; WIDTH: 100
; HEIGHT: 200
; COLOR: blue
Performance Notes¶
- Circular list detection uses the tortoise-and-hare algorithm
- Property list operations are O(n) in list length
- Modify macros may cons new structure or modify in place
flatten
is not tail-recursive for deeply nested structures
Usage Notes¶
- Check
proper-list-p
before operations requiring proper lists - Non-destructive operations create new structure
doplist
iterates property lists- Standard list operations loop forever on circular lists
- Consider
epsilon.map
for large key-value collections
See Also¶
- Common Lisp list operations
- epsilon.sequence - Lazy sequences
- epsilon.map - Hash maps for key-value data