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.
- •
- There's no implied ordering to the associative arrays. If you
used vectors for plotting, you would want to insure the second
component comes after the first, an so on. This isn't possible
since arrays are actually hash tables. For example, you can't get a
range of values between two indices. Nor can you sort an
array.
- •
- Arrays consume lots of memory when the number of elements
becomes large (tens of thousands). This is because each element's
index and value are stored as strings in the hash table.
- •
- The C programming interface is unwieldy. Normally with vectors,
you would like to view the Tcl array as you do a C array, as an
array of floats or doubles. But with hash tables, you must convert
both the index and value to and from decimal strings, just to
access an element in the array. This makes it cumbersome to perform
operations on the array as a whole.
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. Th
create operation can be invoked in one of three forms:
- vector create vecName
- This creates a new vector vecName which initially has no
components.
- vector create vecName(size)
- This second form creates a new vector which will contain
size number of components. The components will be indexed
starting from zero (0). The default value for the components is
0.0.
- vector create
vecName(first:last)
- The last form creates a new vector of indexed first
through last. First and last can be any
integer value so long as first is less than
last.
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(:)
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
Blt_VectorExists,
Blt_GetVector is called to get the
pointer to the vector. Otherwise the routine
Blt_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
lt_ResetVector is called. Blt_ResetVector makes the
changes visible to the Tcl interface and other vector clients (such
as a graph widget).
#include <tcl.h>
#include <blt.h>
Blt_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 (Blt_VectorExists(interp, "data")) {
if (Blt_GetVector(interp, "data", &vecPtr) != TCL_OK) {
return TCL_ERROR;
}
} else {
if (Blt_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 (Blt_ResetVector(vecPtr, newArr, numValues, numValues,
TCL_DYNAMIC) != TCL_OK) {
return TCL_ERROR;
}