SRFI-133: Vector Library (R7RS compatible)

This SRFI proposes a comprehensive library of vector operations accompanied by a freely available and complete reference implementation. The reference implementation is unencumbered by copyright, and useable with no modifications on any Scheme system that is R5RS-compliant. It also provides several hooks for implementation-specific optimization as well.

  1. SRFI-133: Vector Library (R7RS compatible)
  2. Installation
  3. SRFI Description
  4. Vector Library (R7RS compatible)
    1. Constructors
    2. Predicates
    3. Selectors
    4. Iteration
    5. Searching
    6. Mutators
    7. Conversion
  5. Repository
  6. Version History
  7. License

Installation

$ chicken-install srfi-133

or

$ chicken-install srfi-133 -test

if you want to run the tests for the egg in addition.

SRFI Description

For a full description of this SRFI, see the full SRFI document. This documentation covers the API only.

Vector Library (R7RS compatible)

Constructors

[procedure] (make-vector size [fill])

[R7RS-small] Creates and returns a vector of size size. If fill is specified, all the elements of the vector are initialized to fill. Otherwise, their contents are indeterminate.

Example:

(make-vector 5 3)
 ;=> #(3 3 3 3 3)
[procedure] (vector x ...)

[R7RS-small] Creates and returns a vector whose elements are x ....

Example:

(vector 0 1 2 3 4)
 ;=> #(0 1 2 3 4)
[procedure] (vector-unfold f length initial-seed ...)

The fundamental vector constructor. Creates a vector whose length is length and iterates across each index k between 0 and length, applying f at each iteration to the current index and current seeds, in that order, to receive n + 1 values: first, the element to put in the kth slot of the new vector and n new seeds for the next iteration. It is an error for the number of seeds to vary between iterations. Note that the termination condition is different from the unfold procedure of SRFI 1.

Examples:

(vector-unfold (lambda (i x) (values x (- x 1)))
                 10 0)
 ;=> #(0 -1 -2 -3 -4 -5 -6 -7 -8 -9)

Construct a vector of the sequence of integers in the range [0,n).

(vector-unfold values n)
 ;=> #(0 1 2 ... n-2 n-1)

Copy vector.

(vector-unfold (lambda (i) (vector-ref vector i))
                 (vector-length vector))
[procedure] (vector-unfold-right f length initial-seed ...)

Like vector-unfold, but it uses f to generate elements from right-to-left, rather than left-to-right. The first index used is length - 1. Note that the termination condition is different from the unfold-right procedure of SRFI 1.

Examples:

Construct a vector of pairs of non-negative integers whose values sum to 4.

(vector-unfold-right (lambda (i x) (values (cons i x) (+ x 1))) 5 0)
 ;=> #((0 . 4) (1 . 3) (2 . 2) (3 . 1) (4 . 0))

Reverse vector.

(vector-unfold-right (lambda (i x) (values (vector-ref vector x) (+ x 1)))
                       (vector-length vector)
                       0)
[procedure] (vector-copy vec [start [end]])

[R7RS-small] Allocates a new vector whose length is end - start and fills it with elements from vec, taking elements from vec starting at index start and stopping at index end. start defaults to 0 and end defaults to the value of (vector-length vec). SRFI 43 provides an optional fill argument to supply values if end is greater than the length of vec. Neither R7RS-small nor this SRFI requires support for this argument.

Examples:

(vector-copy '#(a b c d e f g h i))
 ;=> #(a b c d e f g h i)

(vector-copy '#(a b c d e f g h i) 6)
 ;=> #(g h i)

(vector-copy '#(a b c d e f g h i) 3 6)
 ;=> #(d e f)
[procedure] (vector-reverse-copy vec [start [end]])

Like vector-copy, but it copies the elements in the reverse order from vec.

Example:

(vector-reverse-copy '#(5 4 3 2 1 0) 1 5)
 ;=> #(1 2 3 4)
[procedure] (vector-append vec ...)

[R7RS-small] Returns a newly allocated vector that contains all elements in order from the subsequent locations in vec ....

Examples:

(vector-append '#(x) '#(y))
 ;=> #(x y)

(vector-append '#(a) '#(b c d))
 ;=> #(a b c d)

(vector-append '#(a #(b)) '#(#(c)))
 ;=> #(a #(b) #(c))
[procedure] (vector-concatenate list-of-vectors)

Appends each vector in list-of-vectors. This is equivalent to:

(apply vector-append list-of-vectors)

However, it may be implemented better.

Example:

(vector-concatenate '(#(a b) #(c d)))
 ;=> #(a b c d)
[procedure] (vector-append-subvectors [vec start end] ...)

Returns a vector that contains every element of each vec from start to end in the specified order. This procedure is a generalization of vector-append.

Example:

(vector-append-subvectors '#(a b c d e) 0 2 '#(f g h i j) 2 4)
 ;=> #(a b h i)

Predicates

[procedure] (vector? x)

[R7RS-small] Disjoint type predicate for vectors: this returns #t if x is a vector, and #f if otherwise.

Examples:

(vector? '#(a b c)) ;=> #t
(vector? '(a b c))  ;=> #f
(vector? #t)        ;=> #f
(vector? '#())      ;=> #t
(vector? '())       ;=> #f
[procedure] (vector-empty? vec)

Returns #t if vec is empty, i.e. its length is 0, and #f if not.

Examples:

(vector-empty? '#(a))   ;=> #f
(vector-empty? '#(()))  ;=> #f
(vector-empty? '#(#())) ;=> #f
(vector-empty? '#())    ;=> #t
[procedure] (vector= elt=? vec ...)

Vector structure comparator, generalized across user-specified element comparators. Vectors a and b are considered equal by vector= iff their lengths are the same, and for each respective element Ea and Eb, (elt=? Ea Eb) returns a true value. elt=? is always applied to two arguments. Element comparison must be consistent with eq; that is, if (eq? Ea Eb) results in a true value, then (elt=? Ea Eb) must also result in a true value. This may be exploited to avoid unnecessary element comparisons. (The reference implementation does, but it does not consider the situation where elt=? is in fact itself eq? to avoid yet more unnecessary comparisons.)

If there are only zero or one vector arguments, #t is automatically returned. The dynamic order in which comparisons of elements and of vectors are performed is left completely unspecified; do not rely on a particular order.

Examples:

(vector= eq? '#(a b c d) '#(a b c d)) ;=> #t
(vector= eq? '#(a b c d) '#(a b d c)) ;=> #f
(vector= = '#(1 2 3 4 5) '#(1 2 3 4)) ;=> #f
(vector= = '#(1 2 3 4) '#(1 2 3 4))   ;=> #t

The two trivial cases.

(vector= eq?) ;=> #t
(vector= eq? '#(a)) ;=> #t

Note the fact that we don't use vector literals in the next two -- it is unspecified whether or not literal vectors with the same external representation are eq?.

(vector= eq? (vector (vector 'a)) (vector (vector 'a)))
 ;=> #f
(vector= equal? (vector (vector 'a)) (vector (vector 'a)))
 ;=> #t

Selectors

[procedure] (vector-ref vec i)

[R7RS-small] Vector element dereferencing: returns the value that the location in vec at i is mapped to in the store. Indexing is based on zero. i must be within the range [0, (vector-length vec)).

Example:

(vector-ref '#(a b c d) 2) ;=> c
[procedure] (vector-length vec)

[R7RS-small] Returns the length of vec, the number of locations reachable from vec. (The careful word 'reachable' is used to allow for 'vector slices,' whereby vec refers to a larger vector that contains more locations that are unreachable from vec. This SRFI does not define vector slices, but later SRFIs may.)

Example:

(vector-length '#(a b c)) ;=> 3

Iteration

[procedure] (vector-fold kons knil vec1 vec2 ...)

The fundamental vector iterator. kons is iterated over each value in all of the vectors, stopping at the end of the shortest; kons is applied as (kons state (vector-ref vec1 i) (vector-ref vec2 i) ...) where state is the current state value -- the current state value begins with knil, and becomes whatever kons returned on the previous iteration --, and i is the current index.

The iteration is strictly left-to-right.

Examples:

Find the longest string's length in vector-of-strings.

(vector-fold (lambda (len str) (max (string-length str) len))
               0 vector-of-strings)

Produce a list of the reversed elements of vec.

(vector-fold (lambda (tail elt) (cons elt tail))
               '() vec)

Count the number of even numbers in vec.

(vector-fold (lambda (counter n)
                 (if (even? n) (+ counter 1) counter))
               0 vec)
[procedure] (vector-fold-right kons knil vec1 vec2 ...)

Similar to vector-fold, but it iterates right to left instead of left to right.

Example:

Convert a vector to a list.

(vector-fold-right (lambda (tail elt) (cons elt tail))
                     '() '#(a b c d))
 ;=> (a b c d)
[procedure] (vector-map f vec1 vec2 ...)

[R7RS-small] Constructs a new vector of the shortest size of the vector arguments. Each element at index i of the new vector is mapped from the old vectors by (f (vector-ref vec1 i) (vector-ref vec2 i) ...). The dynamic order of application of f is unspecified.

Examples:

(vector-map (lambda (x) (* x x))
              (vector-unfold (lambda (i x) (values x (+ x 1))) 4 1))
 ;=> #(1 4 9 16)

(vector-map (lambda (x y) (* x y))
              (vector-unfold (lambda (x) (values x (+ x 1))) 5 1)
              (vector-unfold (lambda (x) (values x (- x 1))) 5 5))
 ;=> #(5 8 9 8 5)

(let ((count 0))
   (vector-map (lambda (ignored-elt)
                 (set! count (+ count 1))
                 count)
               '#(a b)))
 ;=> #(1 2) OR #(2 1)
[procedure] (vector-map! f vec1 vec2 ...)

Similar to vector-map, but rather than mapping the new elements into a new vector, the new mapped elements are destructively inserted into vec1. Again, the dynamic order of application of f unspecified, so it is dangerous for f to apply either vector-ref or vector-set! to vec1 in f.

[procedure] (vector-for-each f vec1 vec2 ...)

[R7RS-small] Simple vector iterator: applies f to the corresponding list of parallel elements from vec1 vec2 ... in the range [0, length), where length is the length of the smallest vector argument passed, In contrast with vector-map, f is reliably applied to each subsequent element, starting at index 0, in the vectors.

Example:

(vector-for-each (lambda (x) (display x) (newline))
                 '#("foo" "bar" "baz" "quux" "zot"))

Displays:

foo bar baz quux zot

[procedure] (vector-count pred? vec1 vec2 ...)

Counts the number of parallel elements in the vectors that satisfy pred?, which is applied, for each index i in the range [0, length) where length is the length of the smallest vector argument, to each parallel element in the vectors, in order.

Examples:

(vector-count even? '#(3 1 4 1 5 9 2 5 6))
 ;=> 3

(vector-count < '#(1 3 6 9) '#(2 4 6 8 10 12))
 ;=> 2
[procedure] (vector-cumulate f knil vec)

Returns a newly allocated vector new with the same length as vec. Each element i of new is set to the result of invoking f on newi-1 and veci, except that for the first call on f, the first argument is knil. The new vector is returned.

Example:

(vector-cumulate + 0 '#(3 1 4 1 5 9 2 5 6))
 ;=> #(3 4 8 9 14 23 25 30 36)

Searching

[procedure] (vector-index pred? vec1 vec2 ...)

Finds & returns the index of the first elements in vec1 vec2 ... that satisfy pred?. If no matching element is found by the end of the shortest vector, #f is returned.

Examples:

(vector-index even? '#(3 1 4 1 5 9))                 ;=> 2
(vector-index < '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) ;=> 1
(vector-index = '#(3 1 4 1 5 9 2 5 6) '#(2 7 1 8 2)) ;=> #f
[procedure] (vector-index-right pred? vec1 vec2 ...)

Like vector-index, but it searches right-to-left, rather than left-to-right, and all of the vectors must have the same length.

[procedure] (vector-skip pred? vec1 vec2 ...)

Finds & returns the index of the first elements in vec1 vec2 ... that do not satisfy pred?. If all the values in the vectors satisfy pred? until the end of the shortest vector, this returns #f. This is equivalent to:

(vector-index (lambda (x1 x2 ...) (not (pred? x1 x1 ...)))
                    vec1 vec2 ...)

Example:

(vector-skip number? '#(1 2 a b 3 4 c d))
 ;=> 2
[procedure] (vector-skip-right pred? vec1 vec2 ...)

Like vector-skip, but it searches for a non-matching element right-to-left, rather than left-to-right, and it is an error if all of the vectors do not have the same length. This is equivalent to:

(vector-index-right (lambda (x1 x2 ...) (not (pred? x1 x1 ...)))
                          vec1 vec2 ...)
[procedure] (vector-binary-search vec value cmp)

Similar to vector-index and vector-index-right, but instead of searching left to right or right to left, this performs a binary search. If there is more than one element of vec that matches value in the sense of cmp, vector-binary-search may return the index of any of them.

cmp should be a procedure of two arguments and return a negative integer, which indicates that its first argument is less than its second, zero, which indicates that they are equal, or a positive integer, which indicates that the first argument is greater than the second argument. An example cmp might be:

(lambda (char1 char2)
  (cond ((char<? char1 char2) -1)
        ((char=? char1 char2) 0)
        (else 1)))
[procedure] (vector-any pred? vec1 vec2 ...)

Finds the first set of elements in parallel from vec1 vec2 ... for which pred? returns a true value. If such a parallel set of elements exists, vector-any returns the value that pred? returned for that set of elements. The iteration is strictly left-to-right.

[procedure] (vector-every pred? vec1 vec2 ...)

If, for every index i between 0 and the length of the shortest vector argument, the set of elements (vector-ref vec1 i) (vector-ref vec2 i) ... satisfies pred?, vector-every returns the value that pred? returned for the last set of elements, at the last index of the shortest vector. The iteration is strictly left-to-right.

[procedure] (vector-partition pred? vec)

A vector the same size as vec is newly allocated and filled with all the elements of vec that satisfy pred? in their original order followed by all the elements that do not satisfy pred, also in their original order.

Two values are returned, the newly allocated vector and the index of the leftmost element that does not satisfy pred.

Mutators

[procedure] (vector-set! vec i value)

[R7RS-small] Assigns the contents of the location at i in vec to value.

[procedure] (vector-swap! vec i j)

Swaps or exchanges the values of the locations in vec at i & j.

[procedure] (vector-fill! vec fill [start [end]])

[R7RS-small] Assigns the value of every location in vec between start, which defaults to 0 and end, which defaults to the length of vec, to fill.

[procedure] (vector-reverse! vec [start [end]])

Destructively reverses the contents of the sequence of locations in vec between start and end. Start defaults to 0 and end defaults to the length of vec. Note that this does not deeply reverse.

[procedure] (vector-copy! to at from [start [end]])

[R7RS-small] Copies the elements of vector from between start and end to vector to, starting at at. The order in which elements are copied is unspecified, except that if the source and destination overlap, copying takes place as if the source is first copied into a temporary vector and then into the destination. This can be achieved without allocating storage by making sure to copy in the correct direction in such circumstances.

[procedure] (vector-reverse-copy! to at from [start [end]])

Like vector-copy!, but the elements appear in to in reverse order. (vector-reverse! target tstart send) would.

[procedure] (vector-unfold! f vec start end initial-seed ...)

Like vector-unfold, but the elements are copied into the vector vec starting at element start rather than into a newly allocated vector. Terminates when end - start elements have been generated.

[procedure] (vector-unfold-right! f vec start end initial-seed ...)

Like vector-unfold!, but the elements are copied in reverse order into the vector vec starting at the index preceding end.

Conversion

[procedure] (vector->list vec [start [end]])

[R7RS-small] Creates a list containing the elements in vec between start, which defaults to 0, and end, which defaults to the length of vec.

[procedure] (reverse-vector->list vec [start [end]])

Like vector->list, but the resulting list contains the elements in reverse of vector.

[procedure] (list->vector proper-list)

[R7RS-small] Creates a vector of elements from proper-list.

[procedure] (reverse-list->vector proper-list)

Like list->vector, but the resulting vector contains the elements in reverse of proper-list.

[procedure] (string->vector string [start [end]])

[R7RS-small] Creates a vector containing the elements in string between start, which defaults to 0, and end, which defaults to the length of string.

[procedure] (vector->string vec [start [end]])

[R7RS-small] Creates a string containing the elements in vec between start, which defaults to 0, and end, which defaults to the length of vec. It is an error if the elements are not characters.

Repository

Github

Version History

1.3
Fixes arg order of vector-cumulate to match fold / vector-fold
1.2
Removes hardcoded .so extension from setup files.
1.1
Incorporates standard README.org into repo.
1.0
Initial release

License

Copyright (C) Taylor Campbell (2003), John Cowan (2015-2016). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.