Tcl does many things well, but handling collections of numbers is not one of them. You could make lists, but for data sets of sizes relevant to scientific graphics which is the primary domain of applicability for PLplot, the extraction time is excessive and burdensome. You could use Tcl arrays, but the storage overhead is astronomical and the lookup time, while better than list manipulation, is still prohibitive.
To cope with this, a Tcl Matrix extension was created for the purpose of making it feasible to work with large collections of numbers in Tcl, in a way which is storage efficient, reasonably efficient for accesses from Tcl, and reasonably compatible with practices used in compiled code.
Much like the Tk widget creation commands, the Tcl matrix
command considers its first argument to be the name of a new command
to be created, and the rest of the arguments to be modifiers. After
the name, the next argument can be float
or
int
or
contractions thereof. Next follow a variable number of size arguments
which determine the size of the matrix in each of its dimensions. For
example:
matrix x f 100 matrix y i 64 64
constructs two matrices. x
is a float matrix, with one
dimension and 100 elements. y
is an integer matrix, and has 2
dimensions each of size 64.
Additionally, an initializer may be specified, with a syntax familiar from C. For example:
matrix x f 4 = { 1.5, 2.5, 3.5, 4.5 }
A Tcl matrix is a command, and as longtime Tcl users know, Tcl
commands are globally accessible. The PLplot Tcl Matrix extension
attempts to lessen the impact of this by registering a variable in the
local scope, and tracing it for insets, and deleting the actual
matrix command when the variable goes out of scope. In this way, a
Tcl matrix appears to work sort of like a variable. It is, however,
just an illusion, so you have to keep this in mind. In particular,
you may want the matrix to outlive the scope in which it was created.
For example, you may want to create a matrix, load it with data, and
then pass it off to a Tk megawidget for display in a spreadsheet like
form. The proc which launches the Tk megawidget will complete, but
the megawidget, and the associated Tcl matrix are supposed to hang
around until they are explicitly destroyed. To achieve this effect,
create the Tcl matrix with the -persist
flag. If present
(can be anywhere on the line), the matrix is not automatically deleted
when the scope of the current proc (method) ends. Instead, you must
explicitly clean up by using either the 'delete' matrix command or renaming
the matrix command name to {}. Now works correctly from within [incr Tcl].
As mentioned above, the result of creating a matrix is that a new command of the given name is added to the interpreter. You can then evaluate the command, providing indices as arguments, to extract the data. For example:
pltcl> matrix x f = {1.5, 2.5, 3.5, 4.5} insufficient dimensions given for Matrix operator "x" pltcl> matrix x f 4 = {1.5, 2.5, 3.5, 4.5} pltcl> x 0 1.500000 pltcl> x 1 2.500000 pltcl> x 3 4.500000 pltcl> x * 1.500000 2.500000 3.500000 4.500000 pltcl> puts "x\[1\]=[x 1]" x[1]=2.500000 pltcl> puts "x\[*\] = :[x *]:" x[*] = :1.500000 2.500000 3.500000 4.500000: pltcl> foreach v [x *] { puts $v } 1.500000 2.500000 3.500000 4.500000 pltcl> for {set i 0} {$i < 4} {incr i} { if {[x $i] < 3} {puts [x $i]} } 1.500000 2.500000
Note from the above that the output of evaluating a matrix indexing operation is suitable for use in condition processing, list processing, etc.
You can assign to matrix locations in a similar way:
pltcl> x 2 = 7 pltcl> puts ":[x *]:" :1.500000 2.500000 7.000000 4.500000: pltcl> x * = 3 pltcl> puts ":[x *]:"
Note that the * provides a means of obtaining an index range, and that it must be separated from the = by a space. Future versions of the Tcl Matrix extension may allow alternative ways of specifying index ranges and may assign the obvious meaning to an expression of the form:
x *= 3
However this has not been implemented yet...
In any event, the matrix
command also supports an
info
subcommand which reports the number of elements in each dimension:
pltcl> x info 4 pltcl> matrix y i 8 10 pltcl> y info 8 10
Normally you will create a matrix in Tcl, and then want to pass it to
C in order to have the data filled in, or existing data to be used in
a computation, etc. To do this, pass the name of the matrix command
as an argument to your C Tcl command procedure. The C code should
include tclMatrix.h
, which has a definition for the
tclMatrix
structure. You fetch a pointer to the
tclMatrix
structure using the
Tcl_GetMatrixPtr
function.
For example, in Tcl:
matrix x f 100 wacky x
and in C:
int wackyCmd( ClientData clientData, Tcl_Interp *interp, int argc, char *argv[] ) { tclMatrix *w; w = Tcl_GetMatrixPtr( interp, argv[1] ); ...
To learn about what else you can do with the matrix once inside
compiled code, read tclMatrix.h
to learn the definition of the
tclMatrix
structure, and see the examples in files like
tclAPI.c
which show many various uses of the Tcl matrix.
Using a Tcl matrix from C++ is very much like using it from C, except
that tclMatrix.h
contains some C++ wrapper classes which are
somewhat more convenient than using the indexing macros which one has
to use in C. For example, here is a tiny snippet from one of the
authors codes in which Tcl matrices are passed in from Tcl to a C++
routine which is supposed to fill them in with values from some
matrices used in the compiled side of the code:
... if (item == "vertex_coords") { tclMatrix *matxg = Tcl_GetMatrixPtr( interp, argv[1] ); tclMatrix *matyg = Tcl_GetMatrixPtr( interp, argv[2] ); Mat2<float> xg(ncu, ncv), yg(ncu, ncv); cg->Get_Vertex_Coords( xg, yg ); TclMatFloat txg( matxg ), tyg( matyg ); for( i=0; i < ncu; i++ ) for( j=0; j < ncv; j++ ) { txg(i,j) = xg(i,j); tyg(i,j) = yg(i,j); }
There are other things you can do too, see the definitions of the
TclMatFloat
and TclMatInt
classes in
tclMatrix.h
.
The Tcl matrix facility provides creation, indexing, and information
gathering facilities. However, considering the scientifically
inclined PLplot user base, it is clear that some users will demand
more. Consequently there is a mechanism for augmenting the Tcl matrix
facility with your own, user defined, extension subcommands. Consider
xtk04.c
. In this extended wish, we want to be able to
determine the minimum and maximum values stored in a matrix. Doing
this in Tcl would involve nested loops, which in Tcl would be
prohibitively slow. We could register a Tcl extension command to do
it, but since the only sensible data for such a command would be a
Tcl matrix, it seems nice to provide this facility as an actual
subcommand of the matrix. However, the PLplot maintainers cannot
foresee every need, so a mechanism is provided to register subcommands
for use with matrix objects.
The way to register matrix extension subcommands is to call
Tcl_MatrixInstallXtnsn
:
typedef int (*tclMatrixXtnsnProc) ( tclMatrix *pm, Tcl_Interp *interp, int argc, char *argv[] ); int Tcl_MatrixInstallXtnsn( char *cmd, tclMatrixXtnsnProc proc );
In other words, make a function for handling the matrix extension
subcommand, with the same function signature (prototype) as
tclMatrixXtnsnProc
, and register the subcommand name along with
the function pointer. For example, xtk04.c has:
int mat_max( tclMatrix *pm, Tcl_Interp *interp, int argc, char *argv[] ) { float max = pm->fdata[0]; int i; for( i=1; i < pm->len; i++ ) if (pm->fdata[i] > max) max = pm->fdata[i]; sprintf( interp->result, "%f", max ); return TCL_OK; } int mat_min( tclMatrix *pm, Tcl_Interp *interp, int argc, char *argv[] ) { float min = pm->fdata[0]; int i; for( i=1; i < pm->len; i++ ) if (pm->fdata[i] < min) min = pm->fdata[i]; sprintf( interp->result, "%f", min ); return TCL_OK; }
Then, inside the application initialization function
(Tcl_AppInit()
to long time Tcl users):
Tcl_MatrixInstallXtnsn( "max", mat_max ); Tcl_MatrixInstallXtnsn( "min", mat_min );
Then we can do things like:
dino 65: xtk04 % matrix x f 4 = {1, 2, 3, 1.5} % x min 1.000000 % x max 3.000000
Your imagination is your only limit for what you can do with this. You could add an FFT subcommand, matrix math, BLAS, whatever.