Version 16 of 2d matrix command

Updated 2004-10-17 06:00:31

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 50 50
  setx mymatrix 3 4 'hello world"
  set a [getx mymatrix 3 4]

So I created a set of five commands that would do that, as seen below. RS gave me some very helpful hints for using upvar, string repeat and other coding tips. Then ak pointed out that the tcllib package struct::matrix is designed to create a 2d matrix just like my commands. So I compared the two approaches using time to see which was fastest. the results are summarized below:

 defx                      struct::matrix 
 --------------------------------------------------------------------------------------
 Test setx = 4424 ms       Test set cell = 321170 ms               defx  7260% faster
 Test addx = 7041 ms       Test add rows = 182172 ms               addx  2587% faster
 Test defx = 11358 ms      Test def matrix = 11003083 ms           defx 96875% faster

These results are pretty dramatic. Please see the source programs below to verify the results.

I've always felt that Tcl desperately needs 2D matrix commands as part of its core. They have so many uses: for 2D graphics in games, for databases, for plotting points in graphs, basically anything that requires two dimensions.

I am proposing that we include only five new commands - defx, setx and getx are general purpose; addx and delx can be used to add and delete rows, and all the rows below are adjusted, as if you had a list stack in a database.

The advantage of these commands is that they create a standard Tcl list variable that you can manipulate, unlike the struct::matrix package. And they are much faster. If compiled into the Tcl core, they would be fast enoughto use in seriuos real-time games and simulation programs, thus broadening the appeal of Tcl.

 # defx - test program

 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 K {a b} {set a}
 proc defx {v a b} {
    upvar $v var; set var [string repeat "{[string repeat {{} } $b]} "  $a]
 }
 proc setx {v a b value} {upvar $v var; lset var $a $b $value}
 proc getx {v a b} {upvar $v var; lindex $var $x $y}
 proc delx {v a} {upvar $v var; set var [lreplace [K $var [set list {}]] $a $a]}
 proc addx {v a} {
    upvar $v var; set new [string repeat {{} } [llength [lindex $var 0]]]
    lappend var $new
    if {$a ne "end"} then {
       set a1 [expr {[llength $var] - 1}] 
       for {set x $a1} {$x > $a} {incr x -1} {lset var $x [lindex $var [expr {$x - 1}]]}
       lset var $a $new
    }
 }

 # test the matrix.  Ranges must be positive integers.

 set a 10; set b 5
 defx wow $a $b                ;# define a matrix: rows/columns
 setx wow 4 1 hello        ;# set row 4, col 1 = "hello"
 setx wow 5 2 big        ;# this row is deleted using 'delx' below
 setx wow 6 3 world
 setx wow 9 4 end
 delx wow 5                ;# delete row 5; now there are only nine rows
 addx wow 5                ;# add row 5 back again (empty); now there are 10 rows

 .text insert end "defx Matrix wow ($a rows, $b cols) \n\n"
 .text insert end "Total rows = [llength $wow] \n\n"
 foreach x $wow {
    foreach y $x {
       .text insert end "#$y \t"
    }
    .text insert end "\n"
 }

 proc set_command {} {
 global wow
 for {set x 0} {$x < 1000} {incr x} {setx wow 4 1 hello}
 }        
 set t [time {set_command} 1]
 .text insert end "\nTest setx = $t\n"

 proc add_command {} {
 global wow
 for {set x 0} {$x < 1000} {incr x} {addx wow end}
 }
 set t [time {add_command} 1]
 .text insert end "Test addx = $t\n"

 proc def_command {} {
 defx neo 1000 1000
 }
 set t [time {def_command} 1]
 .text insert end "Test defx = $t\n"

And here is the struct::matrix program that I used for the time trial:

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

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

 ::struct::matrix wow
 wow add rows $a
 wow add columns $b

 wow set cell 1 4 hello
 wow set cell 2 5 big
 wow set cell 3 6 world
 wow set cell 4 9 end
 wow delete row 5
 wow add rows 1
 .text insert end "::struct::matrix wow ($a rows, $b cols) \n\n"
 .text insert end "Total rows = [wow rows] \n\n"

 for {set x 0} {$x < [wow rows]} {incr x} {
 for {set y 0} {$y < [wow columns]} {incr y} {        

 .text insert end "#[wow get cell $y $x] \t"
 }
 .text insert end "\n"
 }

 proc x_settimer {} {
 for {set x 0} {$x < 1000} {incr x} {wow set cell 1 4 hello}
 }        
 set t [time {x_settimer} 1]
 .text insert end "\nTest set cell = $t\n"

 proc add_command {} {
 for {set x 0} {$x < 1000} {incr x} {wow add rows 1}
 }
 set t [time {add_command} 1]
 .text insert end "Test add rows = $t\n"

 proc def_command {} {
 ::struct::matrix neo
 wow add rows 1000
 wow add columns 1000
 }
 set t [time {def_command} 1]
 .text insert end "Test defx = $t\n"

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


[ Category Graphics

Category Animation ]