./../../src/recursive-assoc-list.lsp

## 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.12

     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:  20:06:42 Tue Nov 10 2020 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-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)