Some examples of the libraries and procedures in the slib namespace of r7rs-libs.

SLIB Introduction and Miscellaneous Libraries

SLIB is a collection of packages of Scheme code, mostly written by Aubrey Jaffer, but some collected from other sources. Looking through the files, some of the original dates go back to 1991, and the precedence parser into 1989. A number of ideas now found in Scheme and captured in the SRFIs can be traced back to SLIB. The repackaged files in r7rs-libs have been adapted to work with R7RS Scheme implementations: using R7RS functionality, the library system, and operations now found in SRFIs.

SLIB’s official documentation is available at http://people.csail.mit.edu/jaffer/slib and is still applicable, with some minor adjustments, to the version provided here.

There are 91 libraries within the R7RS SLIB collection. These fall within four main groups of functionality:

  1. Textual Conversion Packages, including a parser, I/O routines, html and xml processing, pretty-printing of Scheme objects, and procedures to handle times and dates.

  2. Mathematical Libraries, including prime numbers, some random number distributions, plotting functions (to text and eps), root/limit finding libraries and a 3D modelling suite.

  3. Database Packages, which implement a relational database system, and a library for weighted trees.

  4. Other Packages

    1. Data Structures: for enhancing arrays, working with pnm files, an object system and queues.

    2. Sorting and Searching: trees, chapter-order, topological sort, along with some space filling curves and sequence comparisons.

    3. Other Procedures: a library of metric units for computing conversions.

The "examples" and "tests" directories provide (some) examples of using SLIB. Limited documentation for some of the libraries follows.

This section introduces some of the smaller libraries in the SLIB collection. The following chapters go into some of the libraries into more depth, especially those libraries related with a common theme such as array handling or time.

Charplot: (import (slib charplot))

This library provides a way to quickly visualise some data in graphical or histogram form from the REPL or command-line interface. There are two basic functions: histograph and plot, the latter drawing graphs of data or functions.

charplot:dimensions

This parameter allows you to change the size of display. The dimensions are a two-valued list: the height and width in characters. Example below.

histograph

histograph is used to plot histograms of numeric data. Given a list or vector of numbers, it will arrange them into a suitable set of bins and display:

> (histograph '(1 1 2 3 5 5 5 7 8 9) "sample")
           ________________________________________________________________
          |                                                                |
          |                                                                |
         3|-                       I                                       |
          |                        I                                       |
          |                        I                                       |
       2.5|-                       I                                       |
          |                        I                                       |
          |                        I                                       |
         2|-I                      I                                       |
          | I                      I                                       |
          | I                      I                                       |
       1.5|-I                      I                                       |
          | I                      I                                       |
          | I                      I                                       |
         1|-I     I     I          I           I     I     I               |
          | I     I     I          I           I     I     I               |
          | I     I     I          I           I     I     I               |
       0.5|-I     I     I          I           I     I     I               |
          | I     I     I          I           I     I     I               |
          | I     I     I          I           I     I     I               |
         0|-IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII---------------|
          |._____:_____._____:_____._____:_____._____:_____._____:_____.___|
  sample         2           4           6           8           10

> (charplot:dimensions '(10 50))
> (histograph '(1 1 2 3 5 5 5 7 8 9) "sample")
           ___________________________________
         3|-               I                  |
          |                I                  |
         2|-I              I                  |
          | I              I                  |
         1|-I   I   I      I       I   I   I  |
          | I   I   I      I       I   I   I  |
         0|-IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII--|
          |_.____:____.____:____.____:____.___|
  sample        2.5        5        7.5
>

plot

plot can be used in two ways: to display a function, or to display some data.

Plotting a function

plot will plot the values of a single argument function (taking and returning a number). The function should be given as the first argument. The second and third arguments specify the range of x values over which to plot the graph - be careful of infinite values from your function, which will crash plot. An optional fourth argument specifies how many points to plot along the range of x values.

> (plot (lambda (x) (* x x x)) -5 5)
           _________________________________________________________________
       140|-                              :                                 |
          |                               :                                 |
       120|-                              :                             *   |
          |                               :                            *    |
       100|-                              :                           *     |
          |                               :                          *      |
        80|-                              :                         *       |
          |                               :                       **        |
        60|-                              :                      *          |
          |                               :                    **           |
        40|-                              :                   *             |
          |                               :                 ***             |
        20|-                              :               **                |
          |                  ***          :          *****                  |
         0|---------------------*********************-----------------------|
          |                **             :                                 |
       -20|-             **               :                                 |
          |           ***                 :                                 |
       -40|-          *                   :                                 |
          |         **                    :                                 |
       -60|-       *                      :                                 |
          |      **                       :                                 |
       -80|-    *                         :                                 |
          |    *                          :                                 |
      -100|-  *                           :                                 |
          |  *                            :                                 |
      -120|-*                             :                                 |
          |_._____:_____._____:_____._____:_____._____:_____._____:_____.___|
                  -4          -2          0           2           4

> (plot (lambda (x) (* x x x)) -5 5 30)
           _________________________________________________________________
       140|-                              :                                 |
          |                               :                                 |
       120|-                              :                             *   |
          |                               :                                 |
       100|-                              :                           *     |
          |                               :                                 |
        80|-                              :                         *       |
          |                               :                                 |
        60|-                              :                       *         |
          |                               :                     *           |
        40|-                              :                   *             |
          |                               :                 *               |
        20|-                              :               *                 |
          |                    *          :          * *                    |
         0|----------------------*-*-*-*-*:*-*-*-*-*------------------------|
          |                  *            :                                 |
       -20|-              *               :                                 |
          |             *                 :                                 |
       -40|-          *                   :                                 |
          |         *                     :                                 |
       -60|-      *                       :                                 |
          |                               :                                 |
       -80|-    *                         :                                 |
          |                               :                                 |
      -100|-  *                           :                                 |
          |                               :                                 |
      -120|-*                             :                                 |
          |_._____:_____._____:_____._____:_____._____:_____._____:_____.___|
                  -4          -2          0           2           4
Plotting some data

This second form of plot directly plots a list or vector of pairs of (x, y) coordinates. Second and third arguments give labels to the two axes.

> (define data (map (lambda (x) (list x (* x x x))) (iota 11 -5)))
> data
((-5 -125) (-4 -64) (-3 -27) (-2 -8) (-1 -1) (0 0) (1 1) (2 8) (3 27) (4 64) (5 125))
> (plot data "x-values" "y-values")
 y-values  _________________________________________________________________
       140|-                              :                                 |
          |                               :                                 |
       120|-                              :                             *   |
          |                               :                                 |
       100|-                              :                                 |
          |                               :                                 |
        80|-                              :                                 |
          |                               :                                 |
        60|-                              :                       *         |
          |                               :                                 |
        40|-                              :                                 |
          |                               :                 *               |
        20|-                              :                                 |
          |                   *           :           *                     |
         0|-------------------------*-----*-----*---------------------------|
          |                               :                                 |
       -20|-                              :                                 |
          |             *                 :                                 |
       -40|-                              :                                 |
          |                               :                                 |
       -60|-      *                       :                                 |
          |                               :                                 |
       -80|-                              :                                 |
          |                               :                                 |
      -100|-                              :                                 |
          |                               :                                 |
      -120|-*                             :                                 |
          |_._____:_____._____:_____._____:_____._____:_____._____:_____.___|
 x-values         -4          -2          0           2           4

Database

SLIB includes a relational database system, written entirely in Scheme. This is a complex and inter-related set of libraries. The following example (from "slib-examples/db-1.sps") provides an introduction to creating, opening and using a simple database.

Note

The database system stores information in files. It also sets up locks on those files to prevent multiple users trying to change the same data. The code has been tested under Linux, but requires further tests and possible modification on other operating systems.

If your application fails with any errors, it is possible the lock files will remain and must be deleted before you can access the database again: the lock files start ".$" and ".#", and are thus hidden on Linux and similar systems.

More information on SLIB’s database system is available at: http://people.csail.mit.edu/jaffer/slib/Using-Databases.html. After understanding the example below, work through section 6.1.2 on Table Operations.

Creating a database

A database can be created using one or more "backends". Currently, only an association list backend is supported, as provided by (slib alist-table). A database is created to a given filename:

(import (scheme base)
        (slib databases)
        (slib alist-table))

(define db (create-database "mydb" 'alist-table))

The above code imports the necessary libraries, and creates a database in the file "mydb" using an association-list backend. A reference to this database is stored in db.

Next, the database can be opened:

(define rdb (open-database! db))

As we will see below, a database can be opened using either the database reference from the create-database command, or from the filename. So (open-database! "mydb") works just as well.

Databases can be opened in mutable or immutable modes: opened to be changed, or just for reading. The exclamation mark ! indicates that the database is mutable.

Data in a database is stored within tables. Before we can use our new database, we need to create at least one table. A table is defined from three elements:

  1. its name

  2. its primary key field(s)

  3. its other fields

In our example, we make an example with name "testit", a single primary key "id", and two fields "first" and "second". Each field is defined with an associated type (allowed types are described in the online documentation). In this case, our "id" field will be a number, and the two other fields strings.

The basic definition is:

(testit                                  ; the table name
  ((id number))                          ; list of primary key field (name type) pairs
  ((first string) (second string)))      ; list of other field (name type) pairs

When defining a table, we can also provide a list of data to add. The data are simply given in rows, with values for all fields:

((1 "Peter" "Lane")   ; row 1: (id first second)
 (2 "Joe" "Smith"))   ; row 2: (id first second)

Putting the definitions together, the following line of code creates the testit table, and adds some initial data:

(define-tables rdb '(testit ((id number)) ((first string) (second string))
                            ((1 "Peter" "Lane")
                             (2 "Joe" "Smith"))))

Finally, don’t forget to close the database, which ensures everything is saved on disk:

(close-database rdb)

You can examine the file "mydb" to read the definition and data.

Working with a database

Databases should only need constructing once. Afterwards, you will open and work with the existing file. To open an existing database, you need to know its filename:

(define mydb (open-database! "mydb"))   ; open a mutable database

To access information in a table, you need to extract the table of interest:

(define table (open-table! mydb 'testit))

If you were to examine table, you would find it is a procedure. The table procedure takes an operation definition as its argument, and returns a new procedure to perform that operation on some data.

For example, if we wish to retrieve a row of data. The operation definition is 'row:retrieve. The returned procedure then takes the primary key(s) to locate in the database:

((table 'row:retrieve) 1)   ; returns the row with id 1: (1 "Peter" "Lane")

Try displaying all rows (note, the third case will return #f):

(display "Retrieving 1: ") (display ((table 'row:retrieve) 1)) (newline)
(display "Retrieving 2: ") (display ((table 'row:retrieve) 2)) (newline)
(display "Retrieving 3: ") (display ((table 'row:retrieve) 3)) (newline)

A new row can be inserted in a similar way:

((table 'row:insert) '(3 "Jane" "Wheeler"))

Further row operations exist to update and delete.

Operations are also available to work on multiple rows. A useful case is for-each-row:

((table 'for-each-row) (lambda (row) (display "Row: ") (display row) (newline)))

Finally, don’t forget to close the database, which ensures everything is saved on disk:

(close-database mydb)

Dynamic: (import (slib dynamic))

Provides a kind of name-value store, which can be accessed globally. (This is essentially the same as R7RS parameters, with a different syntax.)

make-dynamic creates a dynamic object wrapping a given object.

dynamic? returns true only if the given object is a dynamic.

dynamic-ref returns the value of a given dynamic object.

dynamic-set! changes the value of the given dynamic object.

call-with-dynamic-binding temporarily rebinds a given dynamic object to a new value within the given procedure.

(let ((x (make-dynamic 'a))         ; 1
      (y (make-dynamic 'b)))
  (dynamic? x)                      ; 2
  (eq? 'a (dynamic-ref x))          ; 3
  (eq? 'b (dynamic-ref y))
  (dynamic-set! x 'c)               ; 4
  (call-with-dynamic-binding x 'd   ; 5
    (lambda () (eq? 'd (dynamic-ref x))))
  (eq? 'c (dynamic-ref x)))         ; 6
1 Names x as the dynamic object with value 'a
2 Returns #t as x is a dynamic object
3 Retrieves the value of the given dynamic object, and checks it is correct
4 Changes the value of the given dynamic object
5 Within the lambda x is now bound to 'd
6 and x reverts to holding 'c after the call

Format: (import (slib format))

This library provides an almost complete implementation of Common Lisp’s format function.

The format string syntax is complex. Good descriptions are:

Soundex: (import (slib soundex))

This implementation of the Russell Soundex] algorithm is based on Knuth, Vol. 3 "Sorting and searching", pp.391-2: note, reexported by (robin text)

The soundex algorithm returns a code for a word, and equivalent sounding words should return equivalent codes:

> (soundex "pair")
"P600"
> (soundex "pare")
"P600"
> (soundex "rabbit")
"R130"
> (soundex "racket")
"R230"
> (soundex "smith")
"S530"
> (soundex "smythe")
"S530"

String ports: (import (slib string-port))

Provides procedures to use strings as the current input port, rather like call-with-input-file and call-with-output-file.

call-with-input-string takes a string and a procedure, which must accept a port as its single argument; the string will be used as the input source for that port:

$ (call-with-input-string "(1 2 3)"
                          (lambda (ip) (let ((vals (read ip))) (apply + vals))))
6

call-with-output-string takes a procedure, which must accept a port as its single argument; output written to that port is returned as a string on exit.

$ (call-with-output-string (lambda (ip) (show ip "first" nl "second" nl)))
"first\nsecond\n"

(The show procedure is from the (scheme show) library.)

SSAX XML Parser: (import (slib xml-parse))

This SLIB library exposes various levels of the XML parsing process.

More information on SSAX parsing is available at http://ssax.sourceforge.net/

The main top-level function is ssax:xml→sxml. This takes two arguments: an input port, and an (optionally empty) association list of namespace prefixes to URIs. The function returns an s-expression containing the contents of the XML document read from the input port.

#|kawa:2|# (|ssax:xml->sxml| (open-input-string "<tag>content</tag>") '())
(*TOP* (tag content))
#|kawa:3|# (define *str* "<person gender=\"male\"><name>Peter</name><number>1234</number></person>")
#|kawa:4|# (|ssax:xml->sxml| (open-input-string *str*) '())
(*TOP* (person (@ (gender male)) (name Peter) (number 1234)))

(The vertical bars around the function calls are sometimes required in Kawa.)

Notice the XML strings are converted to s-expressions. Attributes are marked "@". All values are strings.

URIs: (import (slib uri))

A package for creating, decoding and testing various forms of URI.

absolute-path

absolute-path? returns true if the given filename is an absolute path, or false if it depends on the current directory.

> (absolute-path? "/usr/local/bin/scheme")
#t
> (absolute-path? "../filename")
#f

absolute-uri?

absolute-uri? returns true if the given uri is an absolute uri (containing a complete description of the named resource), or false otherwise.

> (absolute-uri? "ftp://example.org/resource.txt")
#t
> (absolute-uri? "resource.txt")
#f

glob-pattern?

glob-pattern? returns true if the given string contains any symbol typically used in a glob, i.e. * ? [ ]

> (glob-pattern? "/home/dir/*/file.txt")
#t
> (glob-pattern? "/home/dir/x/file?.txt")
#t
> (glob-pattern? "/home/dir/x/fil[e].txt")
#t
> (glob-pattern? "/home/dir/x/file.txt")
#f

html syntax

The following four functions generate html text for different purposes:

  • html:anchor creates a named location given a name as a string.

  • html:base returns a string representing the base of given uri.

  • html:isindex creates a string for a search prompt (now deprecated for html forms).

  • html:link takes a uri and title, and returns a link to uri displaying the title.

> (html:anchor "location")    1
<A NAME="location"></A>
> (html:base "http:peterlane.info")
<BASE HREF="http:peterlane.info">
> (html:isindex "Search term: ")
<ISINDEX PROMPT="Search term: ">
> (html:link "http://peterlane.info" "home page")
<A HREF="http://peterlane.info">home page</A>
1 Use "<A HREF=#location>" to link back to this location.

make-uri

make-uri constructs a URI given from 0 to 5 arguments. These are: scheme, authority, path, query, fragment. These are optional from the front, so a single argument will be treated as the fragment; two arguments as a query and then a fragment, etc.

> (make-uri)                                                          ; 1

> (make-uri "xyz")                                                    ; 2
#xyz
> (make-uri "query" "xyz")                                            ; 3
?query#xyz
> (make-uri "http" "peterlane.info" "/files/location" "query" "xyz")  ; 4
http://peterlane.info/files/location?query#xyz
1 Returns the empty string
2 Single argument is a Fragment
3 Query and Fragment
4 All components: the authority in this case is the web address, and the path the relative location of files

null-directory?

null-directory? takes a string and returns true if the string names the current directory (so changing to it would not make any change).

parse-ftp-address

parse-ftp-address converts a string, representing an ftp address, into a list of up to four values: the username, password, remote site, and remote directory. #f is returned if any of these values is not present, or #f instead of a list if the string is not an ftp address.

> (parse-ftp-address "ftp://username@ftp.someplace.site/")
("username" #f "ftp.someplace.site" #f)
> (parse-ftp-address "ftp://username:password@ftp.someplace.site/dir")
("username" "password" "ftp.someplace.site" "dir")

path→uri

path→uri converts a given path (as a string) into a URI with an absolute address.

> (path->uri "filename.txt")
file:/home/NAME/CURRENT-DIRECTORY/filename.txt
> (path->uri "/usr/local/filename.txt")
file:/usr/local/filename.txt

uri→tree

uri→tree takes a URI and returns a list of five values corresponding to the scheme, authority, path, query, fragment:

> (uri->tree "file:/usr/local/filename.txt")
(file #f ("" "usr" "local" "filename.txt") #f #f)

uri:decode-query

uri:decode-query converts a given query string into an association list of key-value pairs:

> (uri:decode-query "name=XXX&date=32")
((date "32") (name "XXX"))

uri:make-path

uri:make-path takes a list of strings, and returns a path component by joining the strings together with a suitable separator:

> (uri:make-path '("a" "b" "c"))
"a/b/c"

uri:splitfields

uri:splitfields splits a string at a given character.

> (uri:splitfields "some text to split" #\space)
'("some" "text" "to" "split")

uric:decode/encode

uric:decode and uric:encode convert a string to and from a form with certain characters rewritten using % octal values:

> (uric:decode "xxx%20yyy")
"xxx yyy"
> (uric:encode "xxx yyy")
"xxx%20yyy"

An optional second argument to uric:encode provides a string of characters to allow.

SLIB Common

This library contains some core functions required throughout the SLIB collection. Apart from (slib:version) users of SLIB probably do not need to call these functions.

add-base-table-implementation

Internal support for database library: adds a supported base implementation to the list of such implementations.

base-table-implementations

Internal support for database library: contains a list of supported base implementations.

char-code-limit

A constant 256: One greater than the largest integer which can be returned by char→integer

gentemp

Returns a new symbol.

identity

A single-parameter identity function.

make-exchanger

Provides mutually exclusive access to its resource: when called, it returns its current content replacing it with its argument.

most-positive-fixnum

Constant #xFFFFFFFF

open-file

A general method to open a port to read or write.

output-port-height

A constant 24: default display height (used e.g. in char-plot)

output-port-width

A constant 79: default display width (used e.g. in char-plot)

provided?

Checks if features, such as big integers, are supported by the current implementation.

slib:version

Returns the current version SLIB.

slib:warn

Displays a warning to the current-error-port before proceeding.

software-type

Returns a symbol indicating the generic operating system type: the choice of possible return types has been reduced for R7RS to 'windows, 'posix or 'unix.

system

Calls the operating system.

tmpnam

A unique string name, used for temporary files.

with-load-pathname

Takes a path and a thunk, evaluates thunk with an internal variable bound to the path.

SLIB Array Libraries

SLIB provides a number of libraries for defining and working with arrays. The array type used is SRFI 63, which was originally a package in SLIB. SRFI 63 provides functions to create, modify and examine arrays.

The collection includes:

  • array-for-each: Functions for applying functions to arrays.

  • array-interpolate: Functions to reduce/resample arrays.

  • byte: A library for arrays of small integers (although R7RS includes bytevector, several libraries in SLIB make use of byte).

  • determinant: A number of standard matrix-algebra functions.

  • subarray: Functions to reduce or access sub-arrays.

SRFI 63: (import (srfi 63))

SLIB uses the array data type from SRFI 63. This section covers some simple examples to illustrate basic use of the array data type. For more details, refer to the SLIB documentation: http://people.csail.mit.edu/jaffer/slib/Arrays.html

Note

When using (srfi 63) you must not export equal? from (scheme base).

There are two basic ways of creating arrays:

  1. an empty array, using make-array

  2. an array with pre-determined content:

    1. using list→array, or

    2. using vector→array

An empty array is constructed from a prototype, which is the initial value in a given array, vector or string; an empty prototype will leave the initial value unspecified. Following the prototype are the dimensions of the array.

For example, to construct a 3x2 array filled with 0:

#|kawa:65|# (define a (make-array #(0) 3 2))  ; 1
#|kawa:66|# a
srfi.63$array@7e6f74c
#|kawa:68|# (array->list a)                   ; 2
((0 0) (0 0) (0 0))                           ; 3
1 The prototype #(0) is used to fill the array with 0s.
2 array→list is provided to convert our array into a list-of-lists form.
3 We end up with 3 rows, each of size two, filled with 0s.

To construct an array with pre-determined content from a list of lists: list→array must also be given the "rank" (number of dimensions) of the array and, as above, a prototype.

#|kawa:71|# (define b (list->array 2 #(0) '((1 2) (3 4) (5 6))))  ; 1
#|kawa:72|# b
srfi.63$array@bcec361
#|kawa:73|# (array->list b)
((1 2) (3 4) (5 6))
1 Constructs an array of rank 2, with 0 as the prototype, and the given values

To construct an array with pre-determined content from a vector. The input vector is simply a vector of the values. The second argument is the prototype, as above. The remaining arguments give the required dimensions of the array.

#|kawa:92|# (define c (vector->array #(1 2 3 4 5 6 7 8 9 10 11 12) #(0) 3 4))
#|kawa:93|# (array->list c)
((1 2 3 4) (5 6 7 8) (9 10 11 12))
#|kawa:94|# (array->vector c)   ; 1
#(1 2 3 4 5 6 7 8 9 10 11 12)
#|kawa:95|# (define c (vector->array #(1 2 3 4 5 6 7 8 9 10 11 12) #(0) 2 3 2))
#|kawa:96|# (array->list c)
(((1 2) (3 4) (5 6)) ((7 8) (9 10) (11 12)))
1 array→vector is an alternative way to retrieve the elements of an array, this time into a vector in row-major order.

Access elements in an array using array-ref and array-set!:

#|kawa:75|# (array->list a)
((0 0) (0 0) (0 0))
#|kawa:78|# (array-ref a 2 1)       ; 1
0
#|kawa:79|# (array-set! a 'x 2 1)
#|kawa:80|# (array-ref a 2 1)
x
#|kawa:81|# (array->list a)
((0 0) (0 0) (0 x))
1 Note order of indexes is the same as in make-array

Information about the array can be found using array-rank to find the number of dimensions of an array, array-dimensions to return a list of the dimensions, and array-in-bounds? returns true if its indices are valid for the given array:

#|kawa:82|# (array-rank a)
2
#|kawa:83|# (array-dimensions a)
(3 2)
#|kawa:84|# (array-in-bounds? a 2 1)
#t
#|kawa:85|# (array-in-bounds? a 1 2)
#f

Finally, array? is used to tell if an object is of this array type or not, and equal? is extended to test if two arrays are equal.

#|kawa:86|# (array? 2)
#f
#|kawa:87|# (array? a)
#t
#|kawa:88|# (array->list a)
((0 0) (0 0) (0 x))
#|kawa:89|# (equal? a 2)
#f
#|kawa:90|# (equal? a '((0 0) (0 0) (0 x)))
#f
#|kawa:91|# (equal? a (list->array 2 #(0) '((0 0) (0 0) (0 x))))
#t

The following libraries provide more functionality when working with arrays.

Array Mapping: (import (slib array-for-each))

This library provides implementations of for-each and map for arrays, as well as functions to work on the indices of an array.

array-for-each

array-for-each takes a procedure and one or more arrays. The procedure is applied to the equivalent elements of each array in row-major order.

The example below simply displays each element in the given array and returns #t.

sash[r7rs]> (define x (list->array 2 #() '((1 2) (3 4))))
#<unspecified>
sash[r7rs]> (array-for-each display x)
1234#t

array-indexes

array-indexes takes an array as an argument and returns a new array of the same dimensions with lists of indexes in each element position.

sash[r7rs]> (define x (list->array 2 #() '((1 2) (3 4))))
#<unspecified>
sash[r7rs]> (array-indexes x)
#<<array> 0x2b52960>
sash[r7rs]> (matrix->lists (array-indexes x))
(((0 0) (0 1)) ((1 0) (1 1)))

array-index-for-each

array-index-for-each takes an array and a procedure and applies the procedure to the indices of each element in the array.

sash[r7rs]> (define x (list->array 2 #() '((1 2) (3 4))))
#<unspecified>
sash[r7rs]> (array-index-for-each x (lambda args (map display args)))
11011000#t

Notice the order of application is not specified.

array-index-map!

array-index-map! takes an array and a procedure and applies the procedure to the indices of each element in the array storing the return value in the corresponding element of the array.

sash[r7rs]> (define a (vector 0 0 0))
#<unspecified>
sash[r7rs]> a
#(0 0 0)
sash[r7rs]> (array-index-map! a (lambda args (apply + args)))
#t
sash[r7rs]> a
#(0 1 2)

In this case, the original array a has the sum of its indices placed into its elements.

array-map!

array-map! takes a result array, a procedure and one or more source arrays. The procedure is applied to the equivalent elements of each source array in row-major order. The return values of the procedure are placed into the respective element of the result array.

In the example below x is used as both the result array and the first source array.

sash[r7rs]> (define x (list->array 2 #() '((1 2) (3 4))))
#<unspecified>
sash[r7rs]> (define y (list->array 2 #() '((5 6) (7 8))))
#<unspecified>
sash[r7rs]> (array-map! x + x y)
#t
sash[r7rs]> (matrix->lists x)                             ; 1
((6 8) (10 12))
1 matrix→lists is from (slib determinant)

array-map

array-map takes an array prototype, a procedure and one or more arrays. The procedure is applied to the equivalent elements of each array in row-major order. The return values of the procedure are placed into the respective element of a new array built using the type of the given prototype.

sash[r7rs]> (define x (list->array 2 #() '((1 2) (3 4))))
#<unspecified>
sash[r7rs]> (define y (list->array 2 #() '((5 6) (7 8))))
#<unspecified>
sash[r7rs]> (matrix->lists (array-map #() + x y))           ; 1
((6 8) (10 12))
1 matrix→lists is from (slib determinant)

array:copy!

array:copy! copies the elements of the second vector or array into the corresponding positions of the first vector or array; the destination must be at least as large as the source in each dimension.

sash[r7rs]> (define v (vector 1 2 3))
#<unspecified>
sash[r7rs]> (define w (vector 4 5 6))
#<unspecified>
sash[r7rs]> v
#(1 2 3)
sash[r7rs]> (array:copy! v w)
#t
sash[r7rs]> v
#(4 5 6)

Array Interpolation: (import (slib array-interpolate))

This library provides two functions to obtain interpolated values or arrays formed from interpolated values of a source array.

interpolate-array-ref

interpolate-array-ref takes an array and a sequence of index positions as real numbers. This is very similar to array-ref, however the index positions need not be integers and so define a point within the array. The function returns an interpolated value based on the coordinates nearest to that point.

For example:

> (define arr (list->array 2 #() '((1 2 3) (4 5 6))))
> (interpolate-array-ref arr 1 0.1)
4.1

The call to interpolate-array-ref selects the row of index 1, which is (4 5 6), and position 0.1 within that row. The value returned is (+ (* 0.9 4) (* 0.1 5)) = 4.1, the intermediate value between 4 and 5 lying a tenth of the way from 4.

Similarly:

> (define arr (list->array 2 #() '((1 2 3) (4 5 6))))
> (interpolate-array-ref arr 0.5 0.25)
2.75

The first index is mid-way between rows index 0 and index 1, and the second index is between the columns index 0 and index 1.

The interpolated value is calculated as:

  1. (+ (* 0.75 4) (* 0.25 5)) = 4.25, a quarter of the way between the 4 and 5 (columns 0 and 1 in row 1)

  2. (+ (* 0.75 1) (* 0.25 2)) = 1.25, a quarter of the way between the 1 and 2 (columns 0 and 1 in row 2)

  3. (+ (* 0.5 4.25) (* 0.5 1.25)) = 2.75, midway between these two values (rows 1 and 2)

resample-array!

resample-array! takes two arrays of equal rank but unequal dimension. The first array is filled with values formed by interpolating its values from the second array.

> (define arr1 (list->array 2 #() '((1 2 3 4) (5 6 7 8))))
> (define arr2 (make-array #() 2 3))
> (array->list arr2)
((0 0 0) (0 0 0))
> (resample-array! arr2 arr1)
> (array->list arr2)
((1 5/2 4) (5 13/2 8))

The interpolation process retains the outer corners. So arr2 preserves the 1, 4, 5 and 8 in its corners. The value of 5/2 in the first row is obtained from the middle value of the first row, computed as: (interpolate-array-ref arr1 0 1.5)

Determinant: (import (slib determinant))

This library is a little misnamed. Apart from determinants, it also provides a range of functions on single and pairs of arrays, here treating the arrays as matrices.

All the functions take either a SRFI-63 array or a list of lists as the matrix definition.

determinant

determinant returns the determinant of a given matrix.

sash[r7rs]> (determinant '((1 2) (3 4)))
-2

matrix→array

matrix→array returns a SRFI-63 array from given matrix definition (e.g. list-of-lists):

sash[r7rs]> (matrix->array '((1 2) (3 4)))
#<<array> 0x2a6a400>

matrix→lists

matrix→lists converts a given matrix definition into a list of lists:

sash[r7rs]> (matrix->lists (matrix->array '((1 2) (3 4))))
((1 2) (3 4))
sash[r7rs]> (matrix->lists '((1 2) (3 4)))
((1 2) (3 4))

matrix:difference

matrix:difference takes two matrices of numbers and returns their element-wise difference. If the matrices are not of equal dimension, returns a matrix of the smallest size.

sash[r7rs]> (matrix:difference '((1 2) (3 4)) '((5 6) (7 8)))
((-4 -4) (-4 -4))
sash[r7rs]> (matrix:difference '((1 2) (3 4)) '((5 6 7) (8 9 10)))
((-4 -4) (-5 -5))

matrix:inverse

matrix:inverse returns the inverse of a given square matrix. If the matrix is singular, the function returns #f.

sash[r7rs]> (matrix:inverse '((1 2) (3 4)))
((-2 1) (3/2 -1/2))
sash[r7rs]> (matrix:inverse '((0 0) (0 0)))
#f

matrix:product

matrix:product takes two arguments, at least one of which must be a matrix. If the other argument is a scalar, returns the element-wise product of the scalar with the matrix. If the other argument is a matrix, returns the matrix product.

sash[r7rs]> (matrix:product '((1 2) (3 4)) '((5 6) (7 8)))
((19 22) (43 50))
sash[r7rs]>  (matrix:product '((1 2) (3 4)) 2)
((2 4) (6 8))
sash[r7rs]> (matrix:product 2 '((1 2) (3 4)))
((2 4) (6 8))

matrix:sum

matrix:sum takes two matrices of numbers and returns their element-wise sum. If the matrices are not of equal dimension, returns a matrix of the smallest size.

sash[r7rs]> (matrix:sum '((1 2) (3 4)) '((5 6) (7 8)))
((6 8) (10 12))
sash[r7rs]> (matrix:sum '((1 2 3) (4 5 6)) '((7 8) (9 10)))
((8 10) (13 15))

transpose

transpose returns a copy of the given matrix with entries flipped about the diagonal.

sash[r7rs]> (transpose '((1 2 3) (4 5 6) (7 8 9)))
((1 4 7) (2 5 8) (3 6 9))

Subarray: (import (slib subarray))

The subarray library is used to identify smaller components of arrays. The returned array shares storage with the original, so a copy should be made if required.

array-trim

array-trim is a simplified version of subarray. Its first argument should be a vector or array. The following arguments, one per dimension of the array, indicate how much to remove from one or other end. Positive values take from the "left", low-index, side and negative values from the "right", high-index side.

#|kawa:46|# (define a (list->array 2 #() '((1 2 3) (4 5 6) (7 8 9))))
#|kawa:47|# (array->list (array-trim a 1 0))  ; 1
((4 5 6) (7 8 9))
#|kawa:48|# (array->list (array-trim a 0 1))  ; 2
((2 3) (5 6) (8 9))
#|kawa:49|# (array->list (array-trim a 2 -1)) ; 3
((7 8))
1 Trim the first row
2 Trim the first column
3 Trim the first two rows and last column

As with subarray, the trimmed array shares storage with the source array.

subarray

subarray precisely selects contiguous subsets of a given array. Its first argument should be an array. There follow one argument per dimension of the array. Each argument is either:

  • an integer: selecting that index of the given dimension

  • a pair of integers: selecting that range of indices in the given dimension

  • #f: making no selection, and so that dimension is not altered.

If there are less arguments than dimensions, the remainder are treated as #f.

#|kawa:4|# (define a (list->array 2 #() '((1 2 3) (4 5 6) (7 8 9))))
#|kawa:11|# (array->list (subarray a 1 #f))           ; 1
(4 5 6)
#|kawa:12|# (array->list (subarray a #f 1))           ; 2
(2 5 8)
#|kawa:14|# (array->list (subarray a #f '(0 1)))      ; 3
((1 2) (4 5) (7 8))
#|kawa:15|# (array->list (subarray a '(1 0) #f))      ; 4
((4 5 6) (1 2 3))
#|kawa:17|# (array->list (subarray a '(2 1) '(0 1)))  ; 5
((7 8) (4 5))
1 Selects a single row in the array: the second row
2 Selects a single column in the array: the second column
3 Selects the first and second columns
4 Selects the last two rows, but in reverse order
5 Selects the last two rows in reverse, and the first two columns

Although the arrays are shared, information is reported as appropriate:

#|kawa:18|# (define b (subarray a '(2 1) '(0 1)))
#|kawa:19|# (array-dimensions a)
(3 3)
#|kawa:20|# (array-dimensions b)
(2 2)
#|kawa:21|# (array-rank b)
2
#|kawa:25|# (array-set! b 'X 0 0)
#|kawa:26|# (array->list a)           ; 1
((1 2 3) (4 5 6) (X 8 9))
#|kawa:27|# (array->list b)
((X 8) (4 5))
1 The source array, a, has also been changed in the appropriate place

There is no array-copy function in SLIB. The following can be used:

#|kawa:39|# (define c (array-map #() identity b))     ; 1
#|kawa:40|# (array->list c)
((X 8) (4 5))
#|kawa:42|# (array-set! c 'y 1 1)
#|kawa:43|# (array->list c)                           ; 2
((X 8) (4 y))
#|kawa:44|# (array->list a)                           ; 3
((1 2 3) (4 5 6) (X 8 9))
#|kawa:45|# (array->list b)
((X 8) (4 5))
1 array-map from (slib array-for-each), identity from (slib common)
2 c is updated
3 a and b are not updated, as c is no longer shared

SLIB Colour Libraries

SLIB includes a number of libraries for defining, storing and manipulating colours.

  • color: defines a datatype for colours

  • color-name: a framework to store/retrieve colour names in SLIB’s database implementation

  • color-space: conversions in colour spaces

  • daylight: models the sun and sky colours

  • nbs-iscc: mapping of colour names to colours

  • resene: mapping of colour names to colours

  • saturate: mapping of colour names to colours

The following are good references for:

NBS/ISCC Colour System: (import (slib nbs-iscc))

This library provides a mapping of colour names to colour values (as defined in (slib color)) based on the NBS/ISCC colour system. For more information see http://tx4.us/nbs-iscc.htm

Two functions are provided:

  1. nbs-iscc-names returns a list of all the names

  2. nbs-iscc returns the named colour

sash[r7rs]> (length (nbs-iscc-names))
267
sash[r7rs]> (list-ref (nbs-iscc-names) 0)
"vivid yellowish green"
sash[r7rs]> (list-ref (nbs-iscc-names) 100)
"vivid purplish red"
sash[r7rs]> (nbs-iscc "vivid yellowish green")
#<<color> 0x2f629a0>
sash[r7rs]> (color->string (nbs-iscc "vivid yellowish green"))    ; 1
"sRGB:39/166/76"
sash[r7rs]> (nbs-iscc (list-ref (nbs-iscc-names) 100))
#<<color> 0x226a900>
sash[r7rs]> (color->string (nbs-iscc (list-ref (nbs-iscc-names) 100)))
"sRGB:206/70/118"
sash[r7rs]> (nbs-iscc "rnd")                                      ; 2
#f
1 color→string is available from the (slib color) library
2 nbs-iscc returns #f if the name is not recognised

Resene Colour System: (import (slib resene))

This library provides a mapping of colour names to colour values (as defined in (slib color)) based on the Resene colour system. For more information see http://www.resene.co.nz

Two functions are provided:

  1. resene-names returns a list of all the names

  2. resene returns the named colour

sash[r7rs]> (import (slib resene))
#<unspecified>
sash[r7rs]> (length (resene-names))
1379
sash[r7rs]> (list-ref (resene-names) 0)
"mantis"
sash[r7rs]> (list-ref (resene-names) 100)
"st tropaz"
sash[r7rs]> (resene "mantis")
#<<color> 0x2d78ea0>
sash[r7rs]> (color->string (resene "mantis"))   ; 1
"sRGB:127/193/92"
sash[r7rs]> (resene (list-ref (resene-names) 100))
#<<color> 0xd6fa40>
sash[r7rs]> (color->string (resene (list-ref (resene-names) 100)))
"sRGB:50/84/130"
sash[r7rs]> (resene "rnd")    ; 2
#f
1 color→string is available from the (slib color) library
2 resene returns #f if the name is not recognised

Note, if you include the Resene RGB Values List in binary form in a program, then you must include its license with your program:

  • Resene RGB Values List

  • For further information refer to http://www.resene.co.nz

  • Copyright Resene Paints Ltd 2001

  • Permission to copy this dictionary, to modify it, to redistribute it, to distribute modified versions, and to use it for any purpose is granted, subject to the following restrictions and understandings.

    1. Any text copy made of this dictionary must include this copyright notice in full.

    2. Any redistribution in binary form must reproduce this copyright notice in the documentation or other materials provided with the distribution.

    3. Resene Paints Ltd makes no warranty or representation that this dictionary is error-free, and is under no obligation to provide any services, by way of maintenance, update, or otherwise.

    4. There shall be no use of the name of Resene or Resene Paints Ltd in any advertising, promotional, or sales literature without prior written consent in each case.

    5. These RGB colour formulations may not be used to the detriment of Resene Paints Ltd.

Saturated Colour System: (import (slib saturate))

This library provides a mapping of saturated colour names to colour values (as defined in (slib color)).

Two functions are provided:

  1. saturate-names returns a list of all the names

  2. saturate returns the named colour

sash[r7rs]> (import (slib saturate))
#<unspecified>
sash[r7rs]> (length (saturate-names))
19
sash[r7rs]> (saturate-names)
("green" "greenish blue" "reddish purple" "purplish blue" "yellow green" "bluish purple" "yellow" "blue" "blue green" "purplish red" "red purple" "yellowish orange" "bluish green" "reddish orange" "purple" "red" "yellowish green" "greenish yellow" "orange")
sash[r7rs]> (saturate "red")
#<<color> 0x35c02c0>
sash[r7rs]> (color->string (saturate "red"))  ; 1
"CIEXYZ:0.735484/0.264516/0"
sash[r7rs]> (saturate "rnd")                  ; 2
#f
1 color→string is available from the (slib color) library
2 saturate returns #f if the name is not recognised

SLIB Mathematics Libraries

SLIB provides a lot of valuable support for various mathematical tasks; Aubrey Jaffer, the main implementor and maintainer of SLIB, also wrote JACAL, a symbolic mathematics program relying heavily on features provided by SLIB.

Factor: (import (slib factor))

Functions for testing if numbers are primes, generating prime numbers, and to factor numbers.

These functions use the Solovay-Strassen primality test:

  • Robert Solovay and Volker Strassen, A Fast Monte-Carlo Test for Primality, SIAM Journal on Computing, 1977, pp 84-85.

factor

factor takes one argument and returns a list of the prime factors of that number: the numbers are in no guaranteed order.

> (factor 5)
(5)
> (factor 100)
(5 5 2 2)

jacobi-symbol

The jacobi-symbol is used in modular arithmetic with one application, as here, being tests for primality. The function returns 0, -1 or 1 given two integers.

> (jacobi-symbol 15 7)
1
> (jacobi-symbol 63 7)
0
> (jacobi-symbol 7 15)
-1

prime:trials

prime:trials controls the likelihood that prime? will accept a composite number as a prime. The probability is 2^(- prime:trials)^

prime:trials is a parameter object, so you can change it to a higher value, if required:

> (prime:trials)
30
> (prime:trials 100)
> (prime:trials)
100

prime?

prime? returns true or false depending on if the given number passes the Solovay-Strassen primality test.

> (prime? 17)
#t
> (prime? 1)
#f

primes<

primes< accepts two arguments: a start value and a count. It returns a list of count prime numbers less than the start value.

> (primes< 18 2)
(13 17)

primes>

primes> accepts two arguments: a start value and a count. It returns a list of count prime numbers greater than the start value.

> (primes> 18 2)
(19 23)

Maths Integer Functions: (import (slib math-integer))

Using the functions in this library means that integer values will return integer results. Using non-integer values generates errors.

For example:

sash[r7rs]> (expt 2.1 3)
9.261000000000001
sash[r7rs]> (integer-expt 2.1 3)
Unhandled exception
  Condition components:
  1. &error
  2. &message integer-expt
  3. &irritants (2.1 3)
  4. &stack-trace
...
sash[r7rs]> (integer-expt 2 3)
8

Additionally, in cases where an inexact result might be expected, these functions will return the nearest integer equivalent:

For example:

sash[r7rs]> (sqrt 9)
3
sash[r7rs]> (sqrt 10)
3.1622776601683795
sash[r7rs]> (integer-sqrt 9)
3
sash[r7rs]> (integer-sqrt 10)
3

Maths Real Functions: (import (slib math-real))

The aim of this library is to prevent non-real numbers creeping into calculations. An error is raised if non-real arguments are given to functions, or if non-real results are returned.

For example:

sash[r7rs]> (sqrt -2)
0.0+1.4142135623730951i
sash[r7rs]> (real-sqrt -2)
Unhandled exception
  Condition components:
  1. &error
  2. &message real-sqrt
  3. &irritants (-2)
  4. &stack-trace

sash[r7rs]> (sin 0+i)
0.0+1.1752011936438014i
sash[r7rs]> (real-sin 0+i)
Unhandled exception
  Condition components:
  1. &error
  2. &message real-sin
  3. &irritants (0+1i)
  4. &stack-trace

Minimize: (import (slib minimize))

The Golden Section Search algorithm can find minima of functions where derivatives are expensive to compute or unavailable.

golden-section-search takes four arguments:

  1. the function to minimize (of one argument)

  2. the lowest value of x to search from

  3. the highest value of x to search to

  4. a stopping condition for the search, which is one of

    1. a positive number defines a target tolerance

    2. a negative number is negated to define the number of iterations

    3. a function of (x0 x1 a b fa fb count) ends the search by returning #t

The function returns a dotted pair: the value of x for the minimum and f(x)

sash[r7rs]> (golden-section-search square -10 10 -100)  ; 1
(4.258043576850981e-21 . 1.8130935102361897e-41)        ; 2
1 Find the minimum of the square value in range (-10, 10) for 100 iterations
2 The minimum is x=0 and (square x)=0

The following example minimizes its function to within the specified tolerance:

sash[r7rs]> (golden-section-search (lambda (x) (+ (* x x x) (* -2 x) -5)) 0 1 (/ 10000)))
(0.8164883855245578 . -6.0886621077391165)

Random Inexact Numbers: (import (slib random-inexact))

A set of functions for generating random inexact numbers. This library is built on top of SRFI 27. All the functions take an optional last argument to define the state. The state is implementation dependent.

The functions all use the default random source, which you can randomise before use using SRFI 27’s:

> (random-source-randomize! default-random-source)

random:exp

random:exp returns an inexact real in an exponential distribution with mean 1.

sash[r7rs]> (random:exp)
0.34154536262153173
sash[r7rs]> (random:exp)
0.05480703829750473
sash[r7rs]> (random:exp)
3.949150883906155

random:hollow-sphere!

random:hollow-sphere! fills a given vector with numbers generated by random:normal, but ensures the vector is on the n-dimensional unit sphere, where n is the length of the given vector.

sash[r7rs]> (define v (vector 0 0 0))
#<unspecified>
sash[r7rs]> (random:hollow-sphere! v)
#t
sash[r7rs]> v
#(0.15379204615082398 0.8499736823380848 0.5038777092443952)
sash[r7rs]> (+ (square (vector-ref v 0)) (square (vector-ref v 1)) (square (vector-ref v 2)))
0.9999999999999999

random:normal

random:normal returns an inexact real in a normal distribution, mean 0 and standard deviation 1.

sash[r7rs]> (random:normal)
0.7092759009436378
sash[r7rs]> (random:normal)
1.0895040600796109
sash[r7rs]> (random:normal)
-0.03239748330944343

random:normal-vector!

random:normal-vector! fills a given vector with numbers generated by random:normal.

sash[r7rs]> (define v (vector 0 0 0))
#<unspecified>
sash[r7rs]> (random:normal-vector! v)
1.838805009717658
sash[r7rs]> v
#(-0.6720177712702253 -0.9211383829955655 0.7339626722027299)

random:solid-sphere!

random:solid-sphere! fills a given vector with numbers generated by random:normal, but ensures the vector fits within the n-dimensional unit sphere, where n is the length of the given vector.

sash[r7rs]> (define v (vector 0 0 0))
#<unspecified>
sash[r7rs]> (random:solid-sphere! v)
0.810489476742576
sash[r7rs]> v
#(-0.7899861662963674 -0.09111554959314132 -0.15656629775645328)
sash[r7rs]> (+ (square (vector-ref v 0)) (square (vector-ref v 1)) (square (vector-ref v 2)))
0.6568931919104545

random:uniform

random:uniform is the same as SRFI 27’s random-real, and returns a uniformly distributed inexact number in the range (0,1).

sash[r7rs]> (random:uniform)
0.7868209548678019
sash[r7rs]> (random:uniform)
0.2504803406880286

Rationalize: (import (slib rationalize))

R7RS includes the function rationalize which takes two arguments, x and y, and returns the simplest rational number differing from x by no more than y.

Note that rationalize only works for rational values of x and y. With inexact values of x or y it returns an inexact value differing from x by no more than y.

> (rationalize 355/113 1/10)
16/5
> (inexact 355/113)
3.14159292035398
> (inexact 16/5)
3.2
> (rationalize 3.14159729 1/10)
3.2

The first example shows an approximation to a value with a 0.1 difference.

However, notice the last example, where an inexact number is returned when seeking an approximation for PI.

The library includes the following two functions:

  • find-ratio which returns a list of the simplest numerator and denominator whose quotient gives a rational number differing from x by no more than y.

  • find-ratio-between which returns a list of the simplest numerator and denominator whose quotient gives a rational number between the given x and y.

These two functions work equally well with exact or inexact values for x and y.

For example, find-ratio can be used to find increasingly better approximations to PI:

#|kawa:1|# (import (slib rationalize))
#|kawa:2|# (find-ratio 3.14159729 0.01)
(22 7)
#|kawa:3|# (find-ratio 3.14159729 0.001)
(201 64)
#|kawa:4|# (find-ratio 3.14159729 0.0001)
(333 106)
#|kawa:5|# (find-ratio 3.14159729 0.00001)
(355 113)

find-ratio-between is used as follows:

#|kawa:6|# (find-ratio-between 2/7 3/5)
(1 2)

SLIB Time/Date Libraries

The R7RS standard has a library (scheme time) which contains three functions: current-second, to obtain the current time in seconds since 1/1/1970; current-jiffy, which returns an implementation-defined fraction of a second from an implementation-specific start point; and jiffies-per-second, which returns the implementation-specific number of "jiffies" per second. The libraries provided in SLIB help convert to and from larger units of time (such as days and years), and also take account of time-zones. SRFI 19] provides a useful standard library for working with dates and times, where available.

SLIB provides a small set of libraries for managing times, dates and time-zones. These reflect Lisp and Posix treatments of time. The libraries use a variety of data formats:

Calendar Time

number of seconds since 1st January 1970 (as used by R7RS current-second)

Decoded Time

nine values for second, minute, hour, date, month, year, day-of-week, daylight-saving-time? and time-zone. Note that months start with January = 1, days of week start with Monday = 0

TM Time

(C’s struct tm) nine values for seconds, minutes, hours, date, month, year, day-of-week, days-in-year, daylight-saving-time?. Note that months start with January = 0, years start with 1900 = 0, days of week start with Sunday = 0

Universal Time

number of seconds since 1st January 1900

The libraries themselves are:

  • (slib common-lisp-time): From Lisp, using Decoded Time and Universal Time.

  • (slib posix-time): From C/Posix, using TM Time and Calendar Time.

  • (slib time-core): main time conversion functions

  • (slib time-zone): computes time-zones and daylight-saving times from the time-zone environment variable or files

  • (slib tzfile): reads from a time-zone file

Time zone information is ultimately read via tzfile which looks for either the environment variable "TZ" or in one of a specified set of paths. (This currently does not appear to support Windows.)

None of these libraries handles leap seconds.

(slib common-lisp-time)

These functions are based on those in Common Lisp to convert between Decoded Time and Universal Time.

get-decoded-time returns the current time in Decoded Time format, as nine values. (get-decoded-time) is short for (decode-universal-time (get-universal-time)).

> (get-decoded-time)
((values) 58 15 18 18 4 2017 1 #f 0)

get-universal-time returns the current time of day as a single integer in Universal Time format representing the number of seconds since 1/1/1900: note for comparison how R7RS’s (current-second) returns a much smaller value (Calendar Time, the number of seconds since 1/1/1970).

> (get-universal-time)
3701527370.61162
> (current-second)
1492540922.30859

decode-universal-time converts a universal time (with an optional time-zone) into the nine values of Decoded Time format.

> (decode-universal-time (get-universal-time))
((values) 41 2 18 18 4 2017 1 #f 0)

The optional time-zone is the number of hours away from GMT. This is backwards from what you might expect: British Summer Time, one hour ahead of GMT, is given by -1.

> (decode-universal-time (get-universal-time) -1)   ; current time in London (BST = GMT+1)
((values) 26 4 19 18 4 2017 1 #f -1)
> (decode-universal-time (get-universal-time) -5.5) ; current time in New Delhi (GMT+5.5)
((values) 54 35 23 18 4 2017 1 #f -11/2)

encode-universal-time takes the first six values of Decoded Time format, and returns a Universal Time representation.

> (encode-universal-time 8 50 21 18 3 2017)
3698862608.0

(slib time-core)

A collection of functions to help in time conversions based around TM Time.

current-time is the same as current-second. This is the Calendar Time, defined relative to 1/1/1970.

> (current-time)
1492540922.30859

difftime subtracts two times, given as seconds.

leap-year? returns true if given year is a leap-year, false otherwise.

> (leap-year? 2000)
#t
> (leap-year? 2004)
#t
> (leap-year? 1900)
#f

offset-time adds two times together, given as seconds.

time:gmtime takes a time in seconds and returns the TM Time information, assuming the location is GMT:

> (time:gmtime (current-time))
#(39 45 18 18 3 117 2 107 0 0 "GMT")            ; 1
1 time is 18:45 39s, 18 is the date, month is April (3), year is 2017 (117+1900), day of week is Tuesday (2), 108 days from 1st January. The last 0 is for the offset due to the time zone.

Note that time:gmtime calls time:split: (time:gmtime tm) = (time:split tm 0 0 "GMT")

(slib posix-time)

This library provides data structures and functions similar to those in C’s "time.h".

asctime converts a TM time into a string representation:

> (asctime (gmtime (current-time)))
"Tue Apr 18 20:18:15 2017\n"

gmtime takes a Calendar time (in seconds) and returns the TM Time information, assuming the location is GMT (the same as time:gmtime).

gtime is short for (asctime (gmtime ..))

> (gtime (current-time))
"Tue Apr 18 20:38:28 2017\n"

localtime converts a Calendar time into a TM Time. An optional second argument specifies the time zone.

> (localtime (current-time) (read-tzfile "Asia/Calcutta"))
#(51 16 2 19 3 117 3 108 0 -19800 #(73 83 84))
> (localtime (current-time))
#(8 47 21 18 3 117 2 107 1 -3600 #(66 83 84))

ctime is short for (asctime (localtime …​))

> (ctime (current-time))
"Tue Apr 18 21:44:32 2017\n"
> (ctime (current-time) (read-tzfile "Asia/Calcutta"))
"Wed Apr 19 02:16:20 2017\n"

mktime converts the TM Time to Calendar time:

> (current-time)
1492548597.89966
> (localtime (current-time))
#(8 50 21 18 3 117 2 107 1 -3600 #(66 83 84))
> (mktime (localtime (current-time)))
1492552289.0

gmktime is the same as mktime, except it assumes a GMT time zone.

(slib time-zone)

read-tzfile takes a path or, if given #f, uses a known path to a time-zone file. The function then calls tzfile:read from (slib tzfile) and returns the result (see below).

read-tzfile is used by time-zone to read the specific time-zone information for a given locality:

> (read-tzfile "Asia/Dili")
#(tz:file "/usr/share/zoneinfo/Asia/Dili" #(#(#(76 77 84) 30140 #f #f #f) #(#(84 76 84) 28800 #f #f #f) #(#(74 83 84) 32400 #f #f #f) #(#(84 76 84) 32400 #f #f #f) #(#(87 73 84 65) 28800 #f #f #f) #(#(84 76 84) 32400 #f #f #f)) #() #(-2147483648 -1830414140 -879152400 -766054800 199897200 969120000) #(0 1 2 3 4 3))
> (read-tzfile "GMT")
#(tz:file "/usr/share/zoneinfo/GMT" #(#(#(71 77 84) 0 #f #f #f)) #() #() #())

time-zone will either:

  • return its argument, if it is a vector (i.e. already a time-zone description)

  • use read-tzfile if the argument is #f or a time-zone file name prefixed with ":"

  • use string→time-zone if the argument is a string without ":" at start

  • return #f if it could not read the time-zone

> (time-zone #(tz:file "/usr/share/zoneinfo/GMT" #(#(#(71 77 84) 0 #f #f #f)) #() #() #()))
#(tz:file "/usr/share/zoneinfo/GMT" #(#(#(71 77 84) 0 #f #f #f)) #() #() #())
> (time-zone ":Asia/Dili")
#(tz:file "/usr/share/zoneinfo/Asia/Dili" #(#(#(76 77 84) 30140 #f #f #f) #(#(84 76 84) 28800 #f #f #f) #(#(74 83 84) 32400 #f #f #f) #(#(84 76 84) 32400 #f #f #f) #(#(87 73 84 65) 28800 #f #f #f) #(#(84 76 84) 32400 #f #f #f)) #() #(-2147483648 -1830414140 -879152400 -766054800 199897200 969120000) #(0 1 2 3 4 3))
> (time-zone "CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00")
#(tz:rule "CST6CDT,M3.2.0/2:00:00,M11.1.0/2:00:00" "CST" "CDT" 21600 18000 (3 2 0 7200) (11 1 0 7200))

(slib tzfile)

There is only one exported function: tzfile:read. This function reads time-zone information from a system-specified file. For example, on Linux, the file may be "/etc/localtime":

> (import (slib tzfile))
> (tzfile:read "/etc/localtime")
("/etc/localtime" #(#(#(76 77 84) -75 #f #f #f) #(#(66 83 84) 3600 #t #t #f)
#(#(71 77 84) 0 #f #t #f) #(#(66 68 83 84) 7200 #t #t #f) #(#(66 83 84) 3600 #f
#f #f) #(#(66 83 84) 3600 #t #t #t) #(#(71 77 84) 0 #f #t #t) #(#(71 77 84) 0
#f #f #f)) #() #(-2147483648 -1691964000 -1680472800 -1664143200 -1650146400
-1633903200 -1617487200 -1601848800 -1586037600 -1570399200 -1552168800
-1538344800 -1522533600 -1507500000 -1490565600 -1473631200 -1460930400
-1442786400 -1428876000 -1410732000 -1396216800 -1379282400 -1364767200
-1348437600 -1333317600 -1315778400 -1301263200 -1284328800 -1269813600
-1253484000 -1238364000 -1221429600 -1206914400 -1189980000 -1175464800
-1159135200 -1143410400 -1126476000 -1111960800 -1095631200 -1080511200
-1063576800 -1049061600 -1032127200 -1017612000 -1001282400 -986162400
-969228000 -950479200 -942012000 -904518000 -896050800 -875487600 -864601200
-844038000 -832546800 -812588400 -798073200 -781052400 -772066800 -764805600
-748476000 -733356000 -719445600 -717030000 -706748400 -699487200 -687996000
-668037600 -654732000 -636588000 -622072800 -605743200 -590623200 -574293600
-558568800 -542239200 -527119200 -512604000 -496274400 -481154400 -464220000
-449704800 -432165600 -417650400 -401320800 -386200800 -369266400 -354751200
-337816800 -323301600 -306972000 -291852000 -276732000 -257983200 -245282400
-226533600 -213228000 -195084000 -182383200 -163634400 -150933600 -132184800
-119484000 -100735200 -88034400 -68680800 -59004000 -37242000 57722400 69818400
89172000 101268000 120621600 132717600 152071200 164167200 183520800 196221600
214970400 227671200 246420000 259120800 278474400 290570400 309924000 322020000
341373600 354675600 372819600 386125200 404269200 417574800 435718800 449024400
467773200 481078800 499222800 512528400 530672400 543978000 562122000 575427600
593571600 606877200 625626000 638326800 657075600 670381200 688525200 701830800
719974800 733280400 751424400 764730000 782874000 796179600 814323600 820454400
828234000 846378000 859683600 877827600 891133200 909277200 922582800 941331600
954032400 972781200 985482000 1004230800 1017536400 1035680400 1048986000
1067130000 1080435600 1099184400 1111885200 1130634000 1143334800 1162083600
1174784400 1193533200 1206838800 1224982800 1238288400 1256432400 1269738000
1288486800 1301187600 1319936400 1332637200 1351386000 1364691600 1382835600
1396141200 1414285200 1427590800 1445734800 1459040400 1477789200 1490490000
1509238800 1521939600 1540688400 1553994000 1572138000 1585443600 1603587600
1616893200 1635642000 1648342800 1667091600 1679792400 1698541200 1711846800
1729990800 1743296400 1761440400 1774746000 1792890000 1806195600 1824944400
1837645200 1856394000 1869094800 1887843600 1901149200 1919293200 1932598800
1950742800 1964048400 1982797200 1995498000 2014246800 2026947600 2045696400
2058397200 2077146000 2090451600 2108595600 2121901200 2140045200) #(2 1 2 1 2
1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
1 2 1 2 1 3 1 3 1 3 1 3 1 3 1 2 1 2 1 3 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2
1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 4 6 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1
2 1 2 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 7 5 6 5 6 5 6
5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6
5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6 5 6))