guile-aa-tree — an AA tree implementation for Guile scheme

This module provides non-mutating insert and delete operations as well as common operations such as walking the tree. It also features "nested" versions of insert, delete, and search functions, that allow operations on nested trees.

Copying this documentation

guile-aa-tree.html - documentation file for Guile AA Tree

Copyright (C) 2017 Christopher Howard

Guile AA Tree is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

Guile AA Tree is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.

Using the module

(use-modules (aa-tree))

Empty trees

The empty tree is represented by a #nil value. For example, to create a tree with one node, you could call this expression:

(aa-insert #nil 'animals 'moose)

Keys

In the unextended operation of the module, it is necessary that every key in a tree must be either a number, string, identifier, or keyword. Furthermore, every key of every node in a tree must be the same type.

If you wish to use other key types or have multiple keys types in a tree, it is possible to do this by defining an appropriate goops comparison methods for the type(s) you wish to use. More specifically, you must define an appropriate equal? method and < method.

Some functions will also take a list of multiple keys instead of a single key, which signals that a nested operation is intended.

Nested operations

A "nested" tree refers to a tree which is the value of a node in another tree. For convenience, aa-delete, aa-insert, and aa-search can operate on a set of nested trees, by receiving a list of keys instead of a single key. aa-insert can even create nested trees in this way. For example, in a game you might want to store data like so:

(aa-insert world-tree (list 'rooms 'kitchen 'object-descriptions 'knife) "A sharp cutting tool.")

Procedures

Scheme procedure: aa-delete tree key

Performs a non-mutating delete on tree. Deletes the node in tree which has key key. If there is no such node, it simply returns tree. O(log n) worst case efficiency.

If a list of keys is provided instead of a single key, aa-delete will attempt a nested tree delete, starting the search with the first key in the list. If a nested tree of some required level does not exist, it will simply return the original tree unmodified.

Scheme procedure: aa-for-each proc tree

Calls proc for each of the key-value pairs in tree. proc must accept an argument of the form (key . value). The calls will be made in order of least to greatest keys. aa-for-each does not return the results of these calls.

Scheme procedure: aa-for-each-reverse proc tree

aa-for-each-reverse operates the same as aa-for-each except that it applies the pairs in reverse order, from greatest key to least key.

Scheme procedure: aa-insert tree key #:optional value)

Performs a non-mutating insert on tree, inserting a node having key key and value value. O(log n) worst case efficiency.

If a list of keys is provided instead of a single key, aa-insert will attempt a nested tree insert, starting the search with the first key in the list. If a nested tree of some required level does not exist, that tree will be created and inserted into the the previous level tree.

Scheme procedure: aa-map fn tree

Maps fn over the key-value pairs in tree, ordered from smallest to greatest key, returning the list of results. So, fn must take an argument of the form (key . value). Unlike aa-for-each, the actual functional calls will not necessarily be executed in the same order as the key-value pairs. O(n) efficiency.

Scheme procedure: aa-map-keys fn tree

The same operation as aa-map, except only the key of each node is presented to fn. O(n) efficiency.

Scheme procedure: aa-map-reverse fn tree

The equivalent of aa-map, but the ordering is from greatest to smallest key. O(n) efficiency.

Scheme procedure: aa-map-values fn tree

The same operation as aa-map, except only the value of each node is presented to fn. O(n) efficiency.

Scheme procedure: aa-modify tree key fn

A functional setter for node values. Uses aa-search to search for a node in TREE, then feeds the value of that node to FN, and then uses aa-insert to insert the node back into TREE, but containing the new value returned from FN. This is an immutable operation provided of course you don't mutate the object passed to FN. This provides a convenient way to modify a value in the tree, without actually mutating a real node in memory.

Scheme procedure: aa-search tree key

Searches for the node in the tree with key key, and returns the value of that node. If no such key is found, a key-not-found exception will be thrown, with the key as the first argument. O(log n) worst case efficiency.

If a list of keys is provided instead of a single key, aa-search will attempt a nested tree search, starting the search with the first key in the list. If a nested tree of some required level does not exist, the function will throw an exception.

Scheme procedure: aa-search-wrapped tree key

Provides the same functionality as aa-search, except that (1) return values are wrapped in a single element list; (2) if the key is not found, aa-search-wrapped returns #nil. This approach allows you to avoid the possibility of throwing an exception, while still allowing you to distinquish between not finding a node, and finding a node which stores #nil as its value.

While this approach does avoid the use of exceptions, note that it is easy to forget to unwrap (i.e., car) a result before using it.

Scheme procedure: aa-to-list tree

Returns the key value pairs of tree in list form, ordering from smallest to greatest key. O(n) efficiency.

Scheme procedure: list-to-aa-tree init-tree lst

Takes keys and values from lst and inserts them into tree init-tree. If init-tree is #nil (empty tree) then this is the same as converting lst into a tree. Argument lst must be a list of the form (KEY VALUE KEY VALUE ...) with zero or more sets of keys and values.

KEYs may be any type accepted by aa-insert. Note that using another list in place of a single key will still signal a nested insert operation.

If a VALUE is itself a list, then VALUE will be processed by list-to-aa-tree, creating another tree stored as the value of that node (a nested tree). If you wish simply to use a list as a node value, then use the identifer _ as the first element of the list. That first _ will be removed before the node is added.

Example:

(read-set! keywords 'prefix)

(define game-data
   (list-to-aa-tree #nil
      '(:troops
          (:infantry
             (:speed medium :armor weak)
           :heavy-infantry
             (:speed slow :armor strong))
        :city-names
          (_ Aberdeen Brighton Cambridge Exeter Glasgow))))

It is not necessary to use the keyword type for keys, but it does make the data structure more readable.