Vector

Name

vector - Vector data type for Tcl

Synopsis

vector create vecName ?vecName …? ?switches?

vector destroy vecName ?vecName …?

vector expr expression

vector names ?pattern …?

Description

The vector command creates a vector of floating point values. The vector’s components can be manipulated in three ways: through a Tcl array variable, a Tcl command, or the C API.

Introduction

A vector is simply an ordered set of numbers. The components of a vector are real numbers, indexed by counting numbers.

Vectors are common data structures for many applications. For example, a graph may use two vectors to represent the X-Y coordinates of the data plotted. The graph will automatically be redrawn when the vectors are updated or changed. By using vectors, you can separate data analysis from the graph widget. This makes it easier, for example, to add data transformations, such as splines. It’s possible to plot the same data to in multiple graphs, where each graph presents a different view or scale of the data.

You could try to use Tcl’s associative arrays as vectors. Tcl arrays are easy to use. You can access individual elements randomly by specifying the index, or the set the entire array by providing a list of index and value pairs for each element. The disadvantages of associative arrays as vectors lie in the fact they are implemented as hash tables.

The vector command tries to overcome these disadvantages while still retaining the ease of use of Tcl arrays. The vector command creates both a new Tcl command and associate array which are linked to the vector components. You can randomly access vector components though the elements of array. Not have all indices are generated for the array, so printing the array (using the parray procedure) does not print out all the component values. You can use the Tcl command to access the array as a whole. You can copy, append, or sort vector using its command. If you need greater performance, or customized behavior, you can write your own C code to manage vectors.

Example

You create vectors using the vector command and its create operation.

# Create a new vector.
vector create y(50)

This creates a new vector named y. It has fifty components, by default, initialized to 0.0. In addition, both a Tcl command and array variable, both named y, are created. You can use either the command or variable to query or modify components of the vector.

# Set the first value.
set y(0) 9.25
puts "y has [y length] components"

The array y can be used to read or set individual components of the vector. Vector components are indexed from zero. The array index must be a number less than the number of components. For example, it’s an error if you try to set the 51st element of y.

# This is an error. The vector only has 50 components.
set y(50) 0.02

You can also specify a range of indices using a colon (:) to separate the first and last indices of the range.

# Set the first six components of y
set y(0:5) 25.2

If you don’t include an index, then it will default to the first and/or last component of the vector.

# Print out all the components of y
puts "y = $y(:)"

There are special non-numeric indices. The index end, specifies the last component of the vector. It’s an error to use this index if the vector is empty (length is zero). The index ++end can be used to extend the vector by one component and initialize it to a specific value. You can’t read from the array using this index, though.

# Extend the vector by one component.
set y(++end) 0.02

The other special indices are min and max. They return the current smallest and largest components of the vector.

# Print the bounds of the vector
puts "min=$y(min) max=$y(max)"

To delete components from a vector, simply unset the corresponding array element. In the following example, the first component of y is deleted. All the remaining components of y will be moved down by one index as the length of the vector is reduced by one.

# Delete the first component
unset y(0)
puts "new first element is $y(0)"

The vector’s Tcl command can also be used to query or set the vector.

# Create and set the components of a new vector
vector create x
x set {0.02 0.04 0.06 0.08 0.10 0.12 0.14 0.16 0.18 0.20}

Here we’ve created a vector x without a initial length specification. In this case, the length is zero. The set operation resets the vector, extending it and setting values for each new component.

There are several operations for vectors. The range operation lists the components of a vector between two indices.

# List the components
puts "x = [x range 0 end]"

You can search for a particular value using the search operation. It returns a list of indices of the components with the same value. If no component has the same value, it returns "".

# Find the index of the biggest component
set indices [x search $x(max)]

Other operations copy, append, or sort vectors. You can append vectors or new values onto an existing vector with the append operation.

# Append assorted vectors and values to x
x append x2 x3 {2.3 4.5} x4

The sort operation sorts the vector. If any additional vectors are specified, they are rearranged in the same order as the vector. For example, you could use it to sort data points represented by x and y vectors.

# Sort the data points
x sort y

The vector x is sorted while the components of y are rearranged so that the original x,y coordinate pairs are retained.

The expr operation lets you perform arithmetic on vectors. The result is stored in the vector.

# Add the two vectors and a scalar
x expr {x+y}
x expr {x*2}

When a vector is modified, resized, or deleted, it may trigger call-backs to notify the clients of the vector. For example, when a vector used in the graph widget is updated, the vector automatically notifies the widget that it has changed. The graph can then redrawn itself at the next idle point. By default, the notification occurs when Tk is next idle. This way you can modify the vector many times without incurring the penalty of the graph redrawing itself for each change. You can change this behavior using the notify operation.

# Make vector x notify after every change
x notify always
# Never notify
x notify never
# Force notification now
x notify now

To delete a vector, use the vector delete command. Both the vector and its corresponding Tcl command are destroyed.

# Remove vector x
vector destroy x

Syntax

Vectors are created using the vector create operation. The create operation can be invoked in one of three forms:

Vector names must start with a letter and consist of letters, digits, or underscores.

# Error: must start with letter
vector create 1abc

You can automatically generate vector names using the “#auto” vector name. The create operation will generate a unique vector name.

set vec [vector create #auto]
puts "$vec has [$vec length] components"

Vector indices

Vectors are indexed by integers. You can access the individual vector components via its array variable or Tcl command. The string representing the index can be an integer, a numeric expression, a range, or a special keyword.

The index must lie within the current range of the vector, otherwise an an error message is returned. Normally the indices of a vector are start from 0. But you can use the offset operation to change a vector’s indices on-the-fly.

puts $vecName(0)
vecName offset -5
puts $vecName(-5)

You can also use numeric expressions as indices. The result of the expression must be an integer value.

set n 21
set vecName($n+3) 50.2

The following special non-numeric indices are available: min, max, end, and ++end.

puts "min = $vecName($min)"
set vecName(end) -1.2

The indices min and max will return the minimum and maximum values of the vector. The index end returns the value of the last component in the vector. The index ++end is used to append new value onto the vector. It automatically extends the vector by one component and sets its value.

# Append an new component to the end
set vecName(++end) 3.2

A range of indices can be indicated by a colon (:).

# Set the first six components to 1.0
set vecName(0:5) 1.0

If no index is supplied the first or last component is assumed.

# Print the values of all the components
puts $vecName(:)

Vector operations

Operator Description
- ! Unary minus and logical NOT. The unary minus flips the sign of each component in the vector. The logical not operator returns a vector of whose values are 0.0 or 1.0. For each non-zero component 1.0 is returned, 0.0 otherwise.
^ Exponentiation.
* / % Multiply, divide, remainder.
+ - Add and subtract.
<< >> Left and right shift. Circularly shifts the values of the vector (not implemented yet).
< > <= >= Boolean less, greater, less than or equal, and greater than or equal. Each operator returns a vector of ones and zeros. If the condition is true, 1.0 is the component value, 0.0 otherwise.
== != Boolean equal and not equal. Each operator returns a vector of ones and zeros. If the condition is true, 1.0 is the component value, 0.0 otherwise.
| Bit-wise OR. (Not implemented).
&& Logical AND. Produces a 1 result if both operands are non-zero, 0 otherwise.
|| Logical OR. Produces a 0 result if both operands are zero, 1 otherwise.
x?y:z If-then-else, as in C. (Not implemented yet).

See the C manual for more details on the results produced by each operator. All of the binary operators group left-to-right within the same precedence level.

Several mathematical functions are supported for vectors. Each of the following functions invokes the math library function of the same name; see the manual entries for the library functions for details on what they do. The operation is applied to all elements of the vector returning the results:

Additional functions are:

Function Description
abs Returns the absolute value of each component.
random Returns a vector of non-negative values uniformly distributed between [0.0, 1.0) using drand48. The seed comes from the internal clock of the machine or may be set manual with the srandom function.
round Rounds each component of the vector.
srandom Initializes the random number generator using srand48. The high order 32-bits are set using the integral portion of the first vector component. All other components are ignored. The low order 16-bits are set to an arbitrary value.

The following functions return a single value:

Function Description
adev Returns the average deviation (defined as the sum of the absolute values of the differences between component and the mean, divided by the length of the vector).
kurtosis Returns the degree of peakedness (fourth moment) of the vector.
length Returns the number of components in the vector.
max Returns the vector’s maximum value.
mean Returns the mean value of the vector.
median Returns the median of the vector.
min Returns the vector’s minimum value.
q1 Returns the first quartile of the vector.
q3 Returns the third quartile of the vector.
prod Returns the product of the components.
sdev Returns the standard deviation (defined as the square root of the variance) of the vector.
skew Returns the skewness (or third moment) of the vector. This characterizes the degree of asymmetry of the vector about the mean.
sum Returns the sum of the components.
var Returns the variance of the vector. The sum of the squared differences between each component and the mean is computed. The variance is the sum divided by the length of the vector minus 1.

The last set returns a vector of the same length as the argument.

Function Description
norm Scales the values of the vector to lie in the range [0.0..1.0].
sort Returns the vector components sorted in ascending order.

Instance operations

You can also use the vector’s Tcl command to query or modify it. The general form is

C language API

You can create, modify, and destroy vectors from C code, using library routines. You need to include the header file rbc.h. It contains the definition of the structure Rbc_Vector, which represents the vector. It appears below.

typedef struct Rbc_Vector_s {
    double *valueArr; /* Array of values (possibly malloc-ed) */
    int numValues; /* Number of values in the array */
    int arraySize; /* Size of the allocated space */
    double min, max; /* Minimum and maximum values in the vector */
    int dirty; /* Indicates if the vector has been updated */
    int reserved; /* Reserved for future use */
} Rbc_Vector;

The field valueArr points to memory holding the vector components. The components are stored in a double precision array, whose size size is represented by arraySize. NumValues is the length of vector. The size of the array is always equal to or larger than the length of the vector. Min and max are minimum and maximum component values.

Library routines

The following routines are available from C to manage vectors. Vectors are identified by the vector name.

C API example

The following example opens a file of binary data and stores it in an array of doubles. The array size is computed from the size of the file. If the vector “data” exists, calling Rbc_VectorExists2, Rbc_GetVector is called to get the pointer to the vector. Otherwise the routine Rbc_CreateVector is called to create a new vector and returns a pointer to it. Just like the Tcl interface, both a new Tcl command and array variable are created when a new vector is created. It doesn’t make any difference what the initial size of the vector is since it will be reset shortly. The vector is updated when Rbc_ResetVector is called. Rbc_ResetVector makes the changes visible to the Tcl interface and other vector clients (such as a graph widget).

#include <tcl.h>
#include <rbc.h>
Rbc_Vector *vecPtr;
double *newArr;
FILE *f;
struct stat statBuf;
int numBytes, numValues;

f = fopen("binary.dat", "r");
fstat(fileno(f), &statBuf);
numBytes = (int)statBuf.st_size;

/* Allocate an array big enough to hold all the data */
newArr = (double *)malloc(numBytes);
numValues = numBytes / sizeof(double);
fread((void *)newArr, numValues, sizeof(double), f);
fclose(f);

if (Rbc_VectorExists2(interp, "data"))  {
    if (Rbc_GetVector(interp, "data", &vecPtr) != TCL_OK) {
        return TCL_ERROR;
    }
} else {
   if (Rbc_CreateVector(interp, "data", 0, &vecPtr) != TCL_OK) {
       return TCL_ERROR;
   }
}
/*
 * Reset the vector. Clients will be notified when Tk is idle.
 * TCL_DYNAMIC tells the vector to free the memory allocated
 * if it needs to reallocate or destroy the vector.
 */
if (Rbc_ResetVector(vecPtr, newArr, numValues, numValues, TCL_DYNAMIC) != TCL_OK) {
    return TCL_ERROR;
}

Incompatibilities

In previous versions, if the array variable isn’t global (i.e. local to a Tcl procedure), the vector is automatically destroyed when the procedure returns.

proc doit {} {
    # Temporary vector x
    vector x(10)
    set x(9) 2.0
      ...
}

This has changed. Variables are not automatically destroyed when their variable is unset. You can restore the old behavior by setting the “-watchunset” switch.