assoc-list/recursive-assoc-list [ Classes ]

[ Top ] [ assoc-list ] [ Classes ]

NAME

 recursive-assoc-list

 File:             recursive-assoc-list.lsp

 Class Hierarchy:  named-object -> linked-named-object -> sclist -> 
                   circular-sclist -> assoc-list -> recursive-assoc-list

 Version:          1.0.10

 Project:          slippery chicken (algorithmic composition)

 Purpose:          Extension of the assoc-list class to allow and
                   automatically instantiate association lists inside of
                   association lists to any level of nesting. E.g.
                   (setf x 
                     '((1 one)
                       (2 two)
                       (3 ((cat "cat")
                           (dog ((mickey mouse)
                                 (donald duck)
                                 (daffy duck)
                                 (uncle ((james dean)
                                         (dean martin)
                                         (fred astaire)
                                         (ginger ((wolfgang mozart)
                                                  (johann bach)
                                                  (george gershwin)))))))
                           (mouse "mouse")))
                       (4 four)))

 Author:           Michael Edwards: m@michael-edwards.org

 Creation date:    March 18th 2001

 $$ Last modified:  15:34:57 Fri Jul  5 2019 CEST

 SVN ID: $Id$

recursive-assoc-list/add [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Add a new element (key/data pair) to the given recursive-assoc-list
 object. 

 If no value is specified for the optional argument, the new element is
 added at the end of the top level. The optional argument allows for the
 FULL-REF to be specified, i.e. a recursive path of keys down to the nested
 level where the new element is to be placed.

ARGUMENTS

 - A key/data pair.
 - A recursive-assoc-list object.

OPTIONAL ARGUMENTS

 - A list that is the FULL-REF, i.e. a recursive path of keys, down to the
   nested level where the new element is to be placed. 

RETURN VALUE

 Returns T if the specified named-object is successfully added to the given
 recursive-assoc-list. 

 Returns an error if an attempt is made to add NIL to the given
 recursive-assoc-list or if the given named-object is already present at the
 same level within the given recursive-assoc-list. 

EXAMPLE

;; Adding an element while specifying no optional argument results in the new
;; element being placed at the end of the top level by default (evident here by
;; the fact that the ref for (MAKERS) is a single-item list) 
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (add '(makers mark) ral)
  (get-all-refs ral))

=> ((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
    (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE)
    (MAKERS))

;; A list that is a path of keys (FULL-REF) to the desired recursive level must
;; be given as the optional argument in order to place the specified element
;; deeper in the given recursive-assoc-list object
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (add '(yellow sky) ral '(four violets))
  (get-all-refs ral))

=> ((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
    (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE)
    (FOUR VIOLETS YELLOW))

;; Attempting to add an element that is already present at the given level of
;; the given recursive-assoc-list object results in an error
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (add '(makers mark) ral)
  (add '(makers mark) ral))

=>
assoc-list::add: Can't add MAKERS to assoc-list with id MIXED-BAG
    because key already exists!
   [Condition of type SIMPLE-ERROR]

;; Attempting to add NIL also results in an error
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (add '() ral))

=>
assoc-list::add: named-object is NIL!
   [Condition of type SIMPLE-ERROR]

SYNOPSIS

(defmethod add (named-object (ral recursive-assoc-list) &optional ref)

recursive-assoc-list/add-empty-parcel [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Add a recursive-assoc-list object with NIL data (an empty level of
 recursion) to the end of the top-level of a given recursive-assoc-list
 object. 

 NB: Adding an empty parcel to a given recursive-assoc-list object will
     cause the method get-all-refs to fail on that recursive-assoc-list
     object. 

ARGUMENTS

 - A recursive-assoc-list object.
 - A symbol that will be the ID of the new, empty recursive-assoc-list
   object that is to be added.

OPTIONAL ARGUMENTS

 - <new-class> The name of an existing subclass of recursive-assoc-list that
 the parcel should be promoted to.

RETURN VALUE

 A recursive-assoc-list object with DATA of NIL (the "empty parcel")

EXAMPLE

;; Add two new empty parcels (the first a recursive-assoc-list, by default, the
;; second a rthm-seq-palette) and return the new list of REFS:
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (add-empty-parcel ral 'bricolage)
  (add-empty-parcel ral 'rsp 'rthm-seq-palette)
  (get-all-refs ral))

Mark set
=> 

((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
 (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE)
 (BRICOLAGE) (RSP))

SYNOPSIS

(defmethod add-empty-parcel ((ral recursive-assoc-list) id &optional new-class)

recursive-assoc-list/assoc-list-id-list [ Functions ]

[ Top ] [ recursive-assoc-list ] [ Functions ]

DESCRIPTION

 Determine whether a given list contains only atoms which could be used as
 assoc-list IDs. To pass the test, a given atom must be either a symbol, a
 number or a string.

ARGUMENTS

 A list.

RETURN VALUE

 T or NIL indicating whether the atoms of the given list are all capable of
 being used as assoc-list IDs. T = all can be used as assoc-list IDs.

EXAMPLE

;; All of the elements in this list are either a symbol, a number or a
;; string. The list therefore returns a T when tested.
(let ((alil '(jim beam 3 "Allegro" 5 flute)))
  (assoc-list-id-list alil))

=> T

;; This list fails, as the last element is a list (and therefore not of type
;; string, number or symbol)
(let ((alil '(jim beam 3 "Allegro" 5 (flute))))
  (assoc-list-id-list alil))

=> NIL

SYNOPSIS

(defun assoc-list-id-list (id-list)

recursive-assoc-list/get-all-refs [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Return a list of all the keys (REFS) in a given recursive-assoc-list
 object. Nested keys are given in FULL-REF form, i.e. a list that is the
 path of keys to the specific key.

 Keys that are not part of nesting-path are also returned as lists
 (single-item lists) by default. An optional argument allows these to be
 returned as individual symbols rather than lists.

 NB This will only work on the top-level object due to the creation of
    references when linking. If you're interested in the full refs of a
    sub-ral, try ths get-this-refs method. 

ARGUMENTS

 - A recursive-assoc-list object.

OPTIONAL ARGUMENTS

 - T or NIL to indicate whether to return single REFS (non-nested keys) as
   lists or as individual symbols. T = as list. Default = T.

RETURN VALUE

 A list.

EXAMPLE

;; By default all keys are returned as lists, even single (non-nested) keys
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-all-refs ral))

=> ((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
    (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE))

;; Setting the optional argument to NIL returns non-nested keys as symbols
;; rather than lists  
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-all-refs ral nil))

=> (JIM WILD (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
    (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE))

SYNOPSIS

(defmethod get-all-refs ((ral recursive-assoc-list) 
                         &optional 
                         (single-ref-as-list t))

recursive-assoc-list/get-data [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Return the named-object (or linked-named-object) that is identified by a
 specified key within a given recursive-assoc-list object.

 NB: This method returns the named object itself, not just the data
     associated with the key (use assoc-list::get-data-data for that). 

ARGUMENTS

 - A symbol that is the key (id) of the named-object sought, or a list of
   symbols that are the path to the desired named-object within the given
   recursive-assoc-list.
 - The recursive-assoc-list object in which it is sought.

OPTIONAL ARGUMENTS

 - T or NIL to indicate whether a warning is printed if the specified key
   cannot be found within the given assoc-list. T = print. Default = T.

RETURN VALUE

 A named-object is returned if the specified key is found within the given
 recursive-assoc-list object. 

 NIL is returned and a warning is printed if the specified key is not found
 in the given recursive-assoc-list object. This applies, too, when a nested
 key is specified without including the other keys that are the path to that
 key (see example).

EXAMPLE

;; Get a named-object from the top-level of the recursive-assoc-list object
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-data 'wild ral))

=> 
NAMED-OBJECT: id: WILD, tag: NIL, 
data: TURKEY

;; A list including all keys that are the path to the specified key is required
;; to get nested named-objects
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-data '(four violets white) ral))

=> 
NAMED-OBJECT: id: WHITE, tag: NIL, 
data: RIBBON

;; Searching for a key that is not present in the given recursive-assoc-list
;; object returns NIL and a warning
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-data 'johnnie ral))

=> NIL
WARNING:
   assoc-list::get-data: Could not find data with key JOHNNIE 
   in assoc-list with id MIXED-BAG

;; Searching for a nested key without specifying the path to that key within a
;; list also returns a NIL and a warning
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-data 'fox ral))

=> NIL
WARNING:
   assoc-list::get-data: Could not find data with key FOX 
   in assoc-list with id MIXED-BAG

SYNOPSIS

(defmethod get-data :around (ids (ral recursive-assoc-list) 
                                 &optional (warn t))

recursive-assoc-list/get-first [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Returns the first named-object in the DATA slot of the given
 recursive-assoc-list object. 

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

 A named-object that is the first object in the DATA slot of the given
 recursive-assoc-list object.

EXAMPLE

(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-first ral))

=> 
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM

SYNOPSIS

(defmethod get-first ((ral recursive-assoc-list))

recursive-assoc-list/get-first-ref [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Get the full reference into the given recursive-assoc-list object of the
 first named-object in the given recursive-assoc-list object. 

 NB: If the <ral> argument happens to be a recursive-assoc-list object that
     is contained within another recursive-assoc-list object (i.e. is a
     nested recursive-assoc-list object), then the result is the reference
     into the top-level recursive-assoc-list object, not the argument.

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

EXAMPLE

;; A simple call returns the first top-level named-object
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-first-ref ral))

=> (JIM)

;; Return the first ref of a nested recursive-assoc-list object
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-first-ref (get-data-data '(four violets) ral)))

=> (FOUR VIOLETS BLUE)

SYNOPSIS

(defmethod get-first-ref ((ral recursive-assoc-list))

recursive-assoc-list/get-last [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Get the last named-object in a given recursive-assoc-list object. 

 NB: This method functions linearly, not hierarchically. The last named
     object is therefore not necessarily the deepest of a nest, but the last
     listed.  

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

 A named-object (or linked-named-object).

EXAMPLE

;; This returns '(white ribbon), not '(fox hole)
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-last ral))

=> 
NAMED-OBJECT: id: WHITE, tag: NIL, 
data: RIBBON

SYNOPSIS

(defmethod get-last ((ral recursive-assoc-list))

recursive-assoc-list/get-last-ref [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Get the last REF (path of nested keys) of the given recursive-assoc-list
 object. 

 NB: This method functions linearly, not hierarchically. The last-ref may
     not be the deepest nested.

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

 Returns a list that is the last REF of the given recursive-assoc-list 
 object.

EXAMPLE

;; Typical usage with nesting
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-last-ref ral))

=> (FOUR VIOLETS WHITE)

;; Returns the last-ref as a list even if the given recursive-assoc-list object
;; contains no nesting
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four roses)))))
  (get-last-ref ral))

=> (FOUR)

SYNOPSIS

(defmethod get-last-ref ((ral recursive-assoc-list))

recursive-assoc-list/get-previous [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Get the previous named-object in the given recursive-assoc-list object by
 specifying the ID of a named-object contained within that given
 recursive-assoc-list object.  

 An optional argument allows for the retrieval of a previous named-object
 that is more than one step back in the given recursive-assoc-list object
 (i.e., not the named-object that immediately precedes the specified key).

 If the number given for the optional <how-many> argument is greater than
 the number of items in the given recursive-assoc-list object, the value
 returned will be a negative number.

 The method proceeds linearly, not hierarchically, when getting previous
 named-objects from further down into nested assoc-lists. In other words,
 the named-object immediately previous to (white ribbon) in this nested list 
 is (fox hole), which is at a deeper level, not (red ...) or (blue velvet),
 which are at the same level: 
 ((blue velvet) 
  (red ((dragon den) 
        (viper nest) 
        (fox hole))) 
  (white ribbon)

 In order to retrieve objects that are nested more deeply, the list that is
 the <keys> argument must consist of the consecutive path of keys leading to
 that object. If only the key of a named object that is deeper in the list
 is given, and not the path of keys to that object, a warning will be
 printed that the given key cannot be found in the list.

 NB: When this method is applied to keys that contain further assoc-list
     objects, the method will drop into the debugger with an error. 

ARGUMENTS

 - A recursive-assoc-list object.
 - A list containing one or more symbols that are either the ID of the
   specified named object or the path of keys to that object within the
   given recursive-assoc-list object.

OPTIONAL ARGUMENTS

 - An integer indicating how many steps back in the given
   recursive-assoc-list from the specified named-object to look when
   retrieving the desired object (e.g. 1 = immediately previous object, 2 =
   the one before that etc.)

RETURN VALUE

 A linked-named-object.

EXAMPLE

;; Get the object immediately previous to that with the key WILD returns the
;; object with key JIM and data BEAM
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(wild)))

=> 
LINKED-NAMED-OBJECT: previous: NIL, this: (JIM), next: (WILD)
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM

;; Attempting to get the previous object from the key FOUR, which contains a
;; nested list, returns an error unless the first key in the nested list is
;; also included
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(four)))

=>
There is no applicable method for the generic function
  #<STANDARD-GENERIC-FUNCTION PREVIOUS (1)>
when called with arguments
  (
NAMED-OBJECT: id: FOUR, tag: NIL, 

(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(four roses)))

=> 
LINKED-NAMED-OBJECT: previous: (JIM), this: (WILD), next: (FOUR ROSES)
NAMED-OBJECT: id: WILD, tag: NIL, 
data: TURKEY

;; The method defines the previous object linearly, not hierarchically; i.e.,
;; the previous object to (white ribbon) here is (fox hole) and not (red ...)
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(four violets white)))

=> 
LINKED-NAMED-OBJECT: previous: (FOUR VIOLETS RED VIPER), 
this: (FOUR VIOLETS RED FOX), 
next: (FOUR VIOLETS WHITE)
NAMED-OBJECT: id: FOX, tag: NIL, 
data: HOLE

;; Use the <how-many> argument to retrieve previous objects further back than
;; the immediate predecessor
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(four violets white) 4))

=> 
LINKED-NAMED-OBJECT: previous: (FOUR ROSES), 
this: (FOUR VIOLETS BLUE), 
next: (FOUR VIOLETS RED DRAGON)
NAMED-OBJECT: id: BLUE, tag: NIL, 
data: VELVET

;; Using a <how-many> value greater than the number of items in the given
;; recursive-assoc-list object returns a negative number
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (get-previous ral '(four violets white) 14))

=> -7

SYNOPSIS

(defmethod get-previous ((ral recursive-assoc-list) keys 
                         &optional (how-many 1))

recursive-assoc-list/get-this-refs [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DATE

 5th July 2019, Essen Werden

DESCRIPTION

 Get the <this> slots of each named-object (or subclass) in a
 recursive-assoc-list (ral). This should work on top-level rals and sub-rals
 alike, though rals with only sub-rals probably won't return anything. Unlike
 get-all-refs, this doesn't traverse recursive objects.

ARGUMENTS

 - the recursive-assoc-list object

RETURN VALUE

 a list of references (most probably simple lists)

SYNOPSIS

(defmethod get-this-refs ((ral recursive-assoc-list))

recursive-assoc-list/link-named-objects [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Create linked-named-objects from the named-objects in the data slots of the
 given recursive-assoc-list object. The linked-named-objects created hold
 keys that serve as pointers to the previous and next objects in the given
 recursive-assoc-list object, whether recursive or not. 

 The optional <previous> and <higher-next> arguments are only for internal
 recursive calls and so shouldn't be given by the user.

ARGUMENTS

 - A recursive-assoc-list object.

OPTIONAL ARGUMENTS

 - <previous>
 - <higher-next>

EXAMPLE

;;; The recursive-assoc-list may not be linked on creation, evident here
;;; through the value of the LINKED slot
(make-ral 'mixed-bag 
                 '((jim beam)
                   (wild turkey)
                   (four ((roses red)
                          (violets ((blue velvet)
                                    (red ((dragon den)
                                          (viper nest)
                                          (fox hole)))
                                    (white ribbon)))))))

=>
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: NIL
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 3, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: NIL, 
data: (
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM
       
NAMED-OBJECT: id: WILD, tag: NIL, 
data: TURKEY
[...]

;; The recursive-assoc-list object and the named-objects it contains are linked
;; after applying the link-named-objects method
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (link-named-objects ral))

=>
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: T
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 3, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: NIL, 
data: (
LINKED-NAMED-OBJECT: previous: NIL, this: (JIM), next: (WILD)
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM
       
LINKED-NAMED-OBJECT: previous: (JIM), this: (WILD), next: (FOUR ROSES)
NAMED-OBJECT: id: WILD, tag: NIL, 
data: TURKEY

RETURN VALUE

 the recursive-assoc-list object

SYNOPSIS

(defmethod link-named-objects ((ral recursive-assoc-list) 
                               &optional previous higher-next)

recursive-assoc-list/lisp-assoc-listp [ Functions ]

[ Top ] [ recursive-assoc-list ] [ Functions ]

DESCRIPTION

 Determine whether a given list can has the structure of a lisp
 assoc-list. This is assessed based on each of the elements being a 2-item
 list, of which the first is a symbol, number or string (qualifies as a
 key). 
 
 The optional argument <recurse-simple-data> allows the data portion of
 key/data pairs to be viewed as flat lists rather than as recursive lists. 

ARGUMENTS

 - A list.

OPTIONAL ARGUMENTS

 T or NIL to indicate whether to consider lists of 2-item lists in the data
 position of a given key/data pair to be a list or a recursive list.
 T = list. Default = T.

RETURN VALUE

 T or NIL. T = the tested list can be considered a Lisp assoc-list.

EXAMPLE

;; A list of 2-item lists, each of whose item are all either a symbol, number, 
;; or string, can be considered a Lisp assoc-list.
(let ((lal '((roses red) (3 "allegro") (5 flute))))
  (lisp-assoc-listp lal))

=> T

;; By default, lists of 2-item lists in the DATA portion of a key/data pair
;; will be considered as a simple list, rather than a recursive list, resulting
;; in the tested list passing as T.
(let ((lal '((1 2) (3 ((4 5) (6 7))) (8 9))))
  (lisp-assoc-listp lal))

=> T

;; Setting the optional argument to NIL will cause the same list to fail with
(let ((lal '((1 2) (3 ((4 5) (6 7))) (8 9))))
  (lisp-assoc-listp lal nil))

=> NIL

SYNOPSIS

(defun lisp-assoc-listp (candidate &optional (recurse-simple-data t))

recursive-assoc-list/make-ral [ Functions ]

[ Top ] [ recursive-assoc-list ] [ Functions ]

DESCRIPTION

 Create a recursive-assoc-list object, which allows and automatically
 instantiates association lists inside of association lists to any level of
 nesting. 

ARGUMENTS

 - A symbol that is the object's ID.
 - A list of nested lists, or a list.

OPTIONAL ARGUMENTS

 keyword arguments:
 - :recurse-simple-data. T or NIL to indicate whether to recursively
   instantiate a recursive-assoc-list in place of data that appears to be a
   simple assoc-list (i.e. a 2-element list). If NIL, the data of 2-element
   lists whose second element is a number or a symbol will be ignored,
   therefore remaining as a list. For example, this data would normally
   result in a recursive call: (y ((2 23) (7 28) (18 2))).  T = replace
   assoc-list data with recursive-assoc-lists. Default = T.
 - :full-ref. Nil or a list representing the path to a nested
   recursive-assoc-list object within the given recursive-assoc-list object,
   starting from the top level of the given object. When NIL, the given
   recursive-assoc-list object itself is the top level.  Default = NIL.
 - :tag. A symbol that is another name, description etc. for the given
   recursive-assoc-list object. The tag may be used for identification but
   not for searching purposes. Default = NIL.
 - :warn-not-found. T or NIL to indicate whether a warning is printed when
   an index which doesn't exist is used for look-up.  Default = T.

RETURN VALUE

 Returns a recursive-assoc-list object.

EXAMPLE

;; Create a recursive-assoc-list object with default keyword argument values 
(make-ral 'mixed-bag 
          '((jim beam)
            (wild turkey)
            (four ((roses red)
                   (violets ((blue velvet)
                             (red ((dragon den)
                                   (viper nest)
                                   (fox hole)))
                             (white ribbon)))))))

=> 
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: NIL
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 3, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: NIL, 
data: (
[...]

;; Use the class's get-all-refs method to show that by default, simple data is
;; recursed. The sublists in the second list in this example are processed as
;; nested lists
(let ((ral (make-ral 'ral-test
                     '((1 one)
                       (2 ((3 4) (5 6)))
                       (3 three)))))
  (get-all-refs ral))

=> ((1) (2 3) (2 5) (3))

;; Using the same data, but setting the :recurse-simple-data argument to NIL
;; will cause the method to process simple data as a unit rather than nested
;; lists 
(let ((ral (make-ral 'ral-test
                     '((1 one)
                       (2 ((3 4) (5 6)))
                       (3 three))
                     :recurse-simple-data nil)))
  (get-all-refs ral))

=> ((1) (2) (3))

SYNOPSIS

(defun make-ral (id ral &key (recurse-simple-data t) (warn-not-found t)
                 (tag nil) (full-ref nil))

recursive-assoc-list/parcel-data [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DATE

 10 Apr 2010

DESCRIPTION

 Put all the data of a given recursive-assoc-list object into a new
 named-object at the top level of that recursive-assoc-list object; i.e. add
 a level of recursion.  This is a means of making a collection of data
 before perhaps adding more with potentially conflicting ids. 

ARGUMENTS

 - A recursive-assoc-list object.
 - A symbol that is new the top-level id for the current data

RETURN VALUE

 The new recursive-assoc-list object.

EXAMPLE

;; Collect all the data contained within the object 'mixed-bag and store it at
;; the top-level of 'mixed-bag within a new named-object with the id 'potpourri 
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (parcel-data ral 'potpourri))

=>
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: NIL
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 1, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: FROM-PARCEL-DATA, 
data: (
NAMED-OBJECT: id: POTPOURRI, tag: NIL, 
data: 
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: NIL
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 3, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: NIL, 
data: (
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM
[...]

SYNOPSIS

(defmethod parcel-data ((ral recursive-assoc-list) new-id)

recursive-assoc-list/r-count-elements [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Return the total number of elements recursively (across all depths) of the
 given recursive-assoc-list object.

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

 An integer.

EXAMPLE

(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (r-count-elements ral))

=> 8

SYNOPSIS

(defmethod r-count-elements ((ral recursive-assoc-list))

recursive-assoc-list/ral-econs [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Automatically create new recursive-assoc-list objects. 

 This method assumes that any existing key that may be referenced will be
 associated with data that is already a list, to the end of which the new
 data will be added (an error will be signalled if this is not the case.)

ARGUMENTS

 - The data which is to be added.
 - The key to which the data is to be added (see above note for cases where
   this key already exists).
 - The recursive-assoc-list object to which this data is to be added.

RETURN VALUE

 The new data added.

EXAMPLE

;;; Make an empty recursive-assoc-list object and add key/data pairs to the top
;;; level. 
(let ((ral (make-ral nil nil)))
  (print (get-all-refs ral))
  (ral-econs 'beam 'jim ral)
  (ral-econs 'turkey 'wild ral)
  (ral-econs 'roses 'four ral)
  (print (get-all-refs ral))
  (print (get-data-data 'wild ral)))

=>
NIL 
((JIM) (WILD) (FOUR)) 
(TURKEY)

;;; Add data to existing keys within a given recursive-assoc-list object
;;; Note that the data VELVET must be a list for this to succeed

(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue (velvet))
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (print (get-all-refs ral))
  (ral-econs 'underground '(four violets blue) ral)
  (print (get-data-data '(four violets blue) ral)))

=>
((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
 (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE)) 
(VELVET UNDERGROUND)

SYNOPSIS

(defmethod ral-econs (data key (ral recursive-assoc-list))

recursive-assoc-list/recursivep [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Check whether the data in a recursive-assoc-list object is really
 recursive. 

ARGUMENTS

 - A recursive-assoc-list object.

RETURN VALUE

 T or NIL to indicate whether or not the tested data is recursive. 
 T = recursive. 

EXAMPLE

;; The data in this recursive-assoc-list object is really recursive, and
;; the method therefore returns T
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (recursivep ral))

=> T

;; The data in this recursive-assoc-list object is not actually recursive, and
;; the method therefore returns NIL
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four roses)))))
  (recursivep ral))

=> NIL

SYNOPSIS

(defmethod recursivep ((ral recursive-assoc-list))

recursive-assoc-list/relink-named-objects [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 This method is essentially the same as the method link-named-objects, but
 resets the LINKED slot to NIL and forces the link-named-objects method to
 be applied again.

ARGUMENTS

 - A recursive-alloc-list object.

RETURN VALUE

 A recursive-alloc-list object.

EXAMPLE

;; Usage as presented here; see the documentation for method link-named-objects
;; for more detail
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (relink-named-objects ral))

=>
RECURSIVE-ASSOC-LIST: recurse-simple-data: T
                      num-data: 8
                      linked: T
                      full-ref: NIL
ASSOC-LIST: warn-not-found T
CIRCULAR-SCLIST: current 0
SCLIST: sclist-length: 3, bounds-alert: T, copy: T
LINKED-NAMED-OBJECT: previous: NIL, this: NIL, next: NIL
NAMED-OBJECT: id: MIXED-BAG, tag: NIL, 
data: (
LINKED-NAMED-OBJECT: previous: NIL, this: (JIM), next: (WILD)
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM
[...]

SYNOPSIS

(defmethod relink-named-objects ((ral recursive-assoc-list))

recursive-assoc-list/remove-data [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DATE

 February 1st 2016, Edinburgh

DESCRIPTION

 Convenience function to allow objects to be removed from a
 recursive-assoc-list (and subclasses e.g. palettes and maps) 

ARGUMENTS

 - the recursive assoc-list object
 - as many keys as you like. If none are passed then all are removed. 

RETURN VALUE

 The recursive-assoc-list object's new data list, i.e. with elements
 removed. Could of course be NIL if you've not passed any keys.

SYNOPSIS

(defmethod remove-data ((ral recursive-assoc-list) &rest keys)

recursive-assoc-list/rmap [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Recurse over the objects in a recursive-assoc-list and call the given
 function for each each named-object. See also assoc-list's map-data method
 which does pretty much the same but acting on each named-object's data
 rather than the named-object itself.

ARGUMENTS

 - the recursive-assoc-list object
 - the function to call (function object)

OPTIONAL ARGUMENTS

 - &rest further arguments to be passed to the function after the
   named-object from the recursive-assoc-list.

RETURN VALUE

 The recursive-assoc-list object

EXAMPLE

(let ((ral (make-ral 'mixed-bag 
                      '((jim beam)
                        (wild turkey)
                        (four ((roses red)
                               (violets ((blue velvet)
                                         (red ((dragon den)
                                               (viper nest)
                                               (fox hole)))
                                         (white ribbon)))))))))
  (rmap ral #'print))
=>
NAMED-OBJECT: id: JIM, tag: NIL, 
data: BEAM
**************
NAMED-OBJECT: id: WILD, tag: NIL, 
data: TURKEY
**************
NAMED-OBJECT: id: ROSES, tag: NIL, 
data: RED
**************
NAMED-OBJECT: id: BLUE, tag: NIL, 
data: VELVET
**************
NAMED-OBJECT: id: DRAGON, tag: NIL, 
data: DEN
**************
NAMED-OBJECT: id: VIPER, tag: NIL, 
data: NEST
**************
NAMED-OBJECT: id: FOX, tag: NIL, 
data: HOLE
**************
NAMED-OBJECT: id: WHITE, tag: NIL, 
data: RIBBON
**************
T

SYNOPSIS

(defmethod rmap ((ral recursive-assoc-list) function &rest arguments)

recursive-assoc-list/set-data [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Replace the named-object associated with a specified key within a given
 recursive-assoc-list object. This method replaces the whole named-object,
 not just the data of that object. 

ARGUMENTS

 - A key present within the given recursive-assoc-list object. This must be
   a list that is the FULL-REF (path of keys) if replacing a nested
   named-object. If replacing a named-object at the top level, the
   key can be given either as a single-item list or an individual symbol. 
 - A key/data pair as a list.
 - The recursive-assoc-list object in which to find and replace the
   named-object associated with the specified key.

RETURN VALUE

 Returns the new named-object.
 
 Returns NIL when the specified key is not found within the given
 recursive-assoc-list object. 

EXAMPLE

;;; Replace a named-object at the top level using a single symbol
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (set-data 'wild '(makers mark) ral))

=> 
NAMED-OBJECT: id: MAKERS, tag: NIL, 
data: MARK

;; The same can be done stating the top-level key as a single-item list. Apply
;; the get-all-refs method in this example to see the change
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (set-data '(wild) '(makers mark) ral)
  (get-all-refs ral))

=> ((JIM) (MAKERS) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
    (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED FOX) (FOUR VIOLETS WHITE))

;; Replace a nested named-object using a list that is the FULL-REF to that
;; object. Print the application of the method as well as the results from
;; applying the get-all-refs method in this example to see the effects
(let ((ral (make-ral 'mixed-bag 
                     '((jim beam)
                       (wild turkey)
                       (four ((roses red)
                              (violets ((blue velvet)
                                        (red ((dragon den)
                                              (viper nest)
                                              (fox hole)))
                                        (white ribbon)))))))))
  (print (set-data '(four violets red fox) '(bee hive) ral))
  (print (get-all-refs ral)))

=>
NAMED-OBJECT: id: BEE, tag: NIL, 
data: HIVE
**************
 
((JIM) (WILD) (FOUR ROSES) (FOUR VIOLETS BLUE) (FOUR VIOLETS RED DRAGON)
 (FOUR VIOLETS RED VIPER) (FOUR VIOLETS RED BEE) (FOUR VIOLETS WHITE))

SYNOPSIS

(defmethod set-data (key new-value (ral recursive-assoc-list))

recursive-assoc-list/set-slot [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 Set the specified slot of an object with a recursive-assoc-list structure
 to the specified value. This is particularly useful for changing the
 parameters of instrument objects within an instrument palette, for example.

 NB Setting, for instance, the high-sounding slot of an instrument will do
 nothing to the highest-written slot so do take care to set all the slots
 you need to in order to effect pitch selection etc. Or better still use the
 set-standard-range function.

ARGUMENTS

 - The name of the slot whose value is to be set.
 - The value to which that slot is to be set.
 - The key within the given recursive-assoc-list object for which the slot
   is to be set.
 - The recursive-assoc-list object in which the slot is to be changed.

RETURN VALUE

 The value to which the slot has been set.

EXAMPLE

(set-slot 'largest-fast-leap 10 'oboe
          +slippery-chicken-standard-instrument-palette+)

=> 10

SYNOPSIS

(defmethod set-slot (slot value id (ral recursive-assoc-list))

recursive-assoc-list/set-standard-instrument-slot [ Methods ]

[ Top ] [ recursive-assoc-list ] [ Methods ]

DESCRIPTION

 A convenience method to set slots of instruments in the
 +slippery-chicken-standard-instrument-palette+.

DATE

 July 20th 2015

EXAMPLE

(set-standard-instrument-slot 'highest-written 'f4 'double-bass)

SYNOPSIS

(defmethod set-standard-instrument-slot (slot value id)