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.1.0

 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:  19:28:13 Tue Feb  6 2024 CET

 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.

 N.B. All new items are added to the end of the data list, unlike in
 assoc-list where that is the default but can be changed. Here it cannot be
 changed. 

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-and-combine-all [ Functions ]

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

DATE

 December 16th 2022

DESCRIPTION

 combine a list of recursive-assoc-lists (ral) into a new ral but adding
 each ral itself into a new ral with a given ID (parcelling). This allows
 each of the rals in the list to have common ids but by parcelling them up we
 can achieve this at the expense of a further level of recursion.

ARGUMENTS

 - a list of rals
 - a list of ids to be used for parcelling. This should be as long as the
   list of rals.

RETURN VALUE

 a new ral

EXAMPLE

;;; this is a bit convoluted but was the original motivation to write this
;;; routine: say you have a bunch of rthm-seq-palettes made from fragments and
;;; organised according to the length in quarter notes of the sequences. You can
;;; parcel them all up into recursive palettes and then into a single palette 
;;; thus:
(let* ((fragments '((1 (q. q.))
                    (1a (+q. q.))
                    (2 (q h))
                    (3 (h h))
                    (4 (q h.))
                    (5 (q w))
                    (6 (q. q.+h))
                    (7 ({ 3 tq tq tq }))
                    (8 ((e.) s - e e -))))
       (rsps (mapcar #'(lambda (refs)
                         (make-rsp-from-fragments fragments refs))
                     ;; each of these lists will become an rsp, the duration of
                     ;; which is the same length (not checked)
                     '(((((3 4) 1))     ; 3/4
                        (((3 4) 2)))
                       ((((4 4) 3))     ; 4/4
                        (((4 4) 4)))
                       ((((5 4) 5))     ; 5/4
                        (((5 4) 6)))
                       ((((2 4) 7) (7) (7)) ; 6/4
                        (((2 4) 7) (7) (8)))
                       ((((4 4) 4) ((3 4) 1a)))))) ; 7/4
       ;; get the duration of the first rthm-seq in each palette and use that as
       ;; an id for the main palette below
       (durs (loop for rsp in rsps collect
                      (round (duration (get-first rsp)))))
       (rsp (parcel-and-combine-all rsps durs 'rthm-seq-palette)))
  (get-data-data '(6 2) rsp))
->  ((((2 4) { 3 TQ TQ TQ }) ({ 3 TQ TQ TQ }) ((E.) S - E E -)))

SYNOPSIS

(defun parcel-and-combine-all (rals ids &optional new-class)
  (unless (= (length rals) (length ids))
    (error "recursive-assoc-list::parcel-and-combine-all: there should be the ~
            same number of rals as ids."))
  (let ((result (parcel-data (clone (first rals)) (pop ids))))
    (when new-class (setq result (sc-change-class result new-class)))
    (loop for ral in (cdr rals) do
             (setf result (combine result (parcel-data ral (pop ids)))))
    result))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; EOF recursive-assoc-list.lsp

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.

 Note that if you want to parcel some palette because of potentially
 conflicting IDs with another (main) palette then combine is the method to
 use rather than add, e.g.
 (combine orig-palette (parcel-data derived-palette 'filtered))

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)