Version 14 of 2d matrix command

Updated 2004-10-15 16:24:27

alove 13 Oct 2004 - What I'm presenting here is essentially a new TCL command that allows you to very easily create a two dimensional matrix and then add or delete data from it.

Currently there is no elegant way to do this is TCL. You either have to create a list-within-a-list using lindex and lreplace/lrange, or else use a combination of arrays and lists, which is equally awkward. Just figuring out how to create a two dimensional matrix is quite a task!

It would be so much nicer if you could just define a matrix and then set or get variables, like so:

  defx mymatrix 1 50 1 50
  setx mymatrix 3 4 "wow!"
  set a [getx mymatrix 3 4]

So I created this code, which allows you to do just that:

 package require Tcl 8.4
 wm title . "Matrix"
 text .text -height 20 -width 50 -font arial -padx 10 -pady 10
 pack .text -expand yes -fill both

 proc defx {varname a b} {
    upvar 1 $varname var
    set var [string repeat "{[string repeat {{0} } $b]} " $a]
 }

 proc setx {varname a b value} {
    upvar 1 $varname var
    lset var $a $b $value
 }

 proc getx {varname a b} {
    upvar 1 $varname var
    lindex $var $x $y
 }

 proc delx {varname a} {
    upvar 1 $varname var
    set a1 [expr {[llength $var] -1}] 
    for {set x $a} {$x <= $a1} {incr x} {
       set x1 [expr {$x + 1}]
       lset var $x [lindex $var $x1]
    }
    set b1 [llength [lindex $var 0]]
    set y [string repeat {{0} } $b1]
    lset var $a1 $y
 }

 # test the matrix.  Ranges must be positive integers.
 set a 10; set b 5

 defx wow $a $b
 setx wow 4 1 hello
 setx wow 5 2 big
 setx wow 6 3 world
 setx wow 9 4 end
 delx wow 5
 .text insert end "Matrix wow ($a rows, $b cols) \n\n"

 foreach x $wow {
 foreach y $x {
 .text insert end "$y \t"
 }
 .text insert end "\n"        
 }

Essentially there are three procedures here, plus an optional fourth one called delx that allows you to delete rows, and have all the rows below adjusted, as if you had a list stack in a database.

This 2D matrix has a lot of uses: for 2D graphics in games, for databases, for plotting points in graphs, basically anything that requires two dimensions.

I think this would be a really useful new command if it were compiled into the TCL core for speed. As it is, it can be used for any matrix that doesn't require super speed. You just put the three (or four) procedures into your code and you've got these matrix commands available.

But please let me know what you think. Any comments are appreciated. alove 13 Oct 2004 -


RS: Since 8.4, Tcl has indeed some splendid support for multi-dimensional lists, where matrixes are included for "multi"==2, and they are pure values - as opposed to the array approach above, which requires a variable name being passed around. Consider

 set element [lindex $matrix $x $y]

and

 lset matrix $x $y $value

That is to say, once you have a matrix represented as list of lists. From Tcl 8.5, there'll be lrepeat, but before that, nested string repeats give you an initialized matrix, at the cost of string->list shimmering once:

 % set x 3; set y 4
 4
 % string repeat "{[string repeat {{} } $x]} " $y
 {{} {} {} {} } {{} {} {} {} } {{} {} {} {} } 

But after the first time you use this string with lindex and lset, it'll be just a perfect list of lists...

alove 15 Oct 2004 - I rewrote the above script using only lists, as you suggested. But I still think it would be better to have actual matrix commands instead of recycling list commands. Programmers would have a real hard time figuring out how to create a matrix unless they read it in some TCL book. But if there were specific commands, it would be obvious, and people would use it a lot.

RS same day - Well yes, but your above code still requires all matrixes to be global variables. However, after the simplifications you made, you'll notice that the proc bodies become very simple - so simple that you can do

 interp alias {} setx {} lset

For the getx case, if you want to explicitly name the variable (instead of $-dereferencing it), upvar allows to use both global and local variables, and makes the code simpler:

 proc getx {varName a b} {
    upvar 1 $varName var
    lindex $var $x $y
 }

Similar with the "constructor":

 proc defx {varName a b} {
    upvar 1 $varName var
    set var [string repeat "{[string repeat {{0} } $b]} " $a]
 }

Local variables have the big advantage that you don't have to worry about side effects or garbage collection.

alove 15 Oct - Okay, I've changed the script again to use upvar. The code is getting very small! What would be the advantage of using interp alias? Is it faster?


ak: See also Tcllib, and its struct::matrix.


AM Interesting - I have been contemplating an investigation into the performance of various ways to create and update a matrix with numbers - this in response to Partial Differential Equations


alove 15 Oct - Yes, I would like to do timing tests on two versions of defx using upvar vs. global (passing the var) to see how much faster it is, then compare that to the struct::matrix package. Should be fun.


[ Category Graphics

Category Animation ]