Arjen Markus (7 september 2005) Inspired by Lars Hellstrom's "essay" On mathematical notation, I sat down last night and concocted a straightforward implementation of *vector spaces* in Tcl. The idea is described very clearly by Lars, so I will only explain a few implementation details.

**First:** the code below only builds procedures that takes vectors (lists) of whatever data object the underlying *field* represents. You can not easily do combinations of scalar and vector arguments this way (for instance, the underlying field may use a list to represent the "number", and that is indistinguishable by [list] from a list of such numbers - you could do that if there is an "isscalar" function ...

**Second:** th code assumes that all operations act on individual components of the vectors or two individual components from two vectors. There is no such thing as matrix multiplication or a dot product in the code.

**Third:** the code uses the *very important* fact that [namespace] and [proc] are completely ordinary commands in Tcl. You can therefore use them inside another procedure to dynamically create procedures and namespaces of your liking.

# vectorspace.tcl -- # Define vector spaces over arbitrary fields # # Notes: # Procedures of one and two arguments only are expanded # No provision for scalar-vector operations or for reducing # operations such as summing all components. All operations # are considered to be component-by-component. # # vectorspace -- # Set up the vector space of given dimension over a given field # Arguments: # name Name of the vector space # field Name of the algebraic construct (a field or some other # construct like a ring) # dim Number of dimensions # Result: # Name of the new vector space # Note: # The field is represented by a namespace containing one or # more exported procedures of one or two arguments. The # vector space is represented by procedures based on these # procedures that are expanded to take care of vector-valued # arguments # proc vectorspace {name field dim} { # # Check that the namespace "field" exists, then construct # a namespace whose procedures are vector-based extensions # of the original # if { ! [namespace exists $field] } { error "Field $field is unknown" } namespace eval $name {} # # A constructor procedure # proc ${name}::vector {args} [string map [list DIM $dim] { if { [llength $args] != DIM } { error "Wrong number of arguments" } return $args }] ;# End string map foreach op [namespace eval $field {namespace export}] { set args_op [namespace eval $field [list info args $op]] switch -- [llength $args_op] { "1" { namespace eval $name [list namespace export $op] proc ${name}::$op {v} [string map [list FIELD $field OP $op DIM $dim] { set r {} if { [llength $v] != DIM } { error "Argument has wrong dimension" } foreach c $v { lappend r [FIELD::OP $c] } return $r }] ;# End string map } "2" { namespace eval $name [list namespace export $op] proc ${name}::$op {u v} [string map [list FIELD $field OP $op DIM $dim] { set r {} if { [llength $u] != DIM || [llength $v] != DIM } { error "At least one argument has wrong dimension" } foreach c $u d $v { lappend r [FIELD::OP $c $d] } return $r }] ;# End string map } "default" { # Ignore } } } return $name } # # Create a few functions to test this idea # namespace eval float { proc cos {x} {expr {cos($x)}} proc + {x y} {expr {$x+$y}} proc - {x y} {expr {$x-$y}} proc * {x y} {expr {$x*$y}} namespace export cos + - * } # main -- # Test the vectorspace procedure # # 1. Create a three-dimensional vector space # 2. Create a space of 3x3 matrices # vectorspace R^3 float 3 vectorspace R3x3 R^3 3 puts "3D vectors:" set a [R^3::vector 1 2 3] set b [R^3::vector 4 1 2] set c [R^3::vector 2 5 1] puts "Sum: [R^3::+ $a $b]" puts "Difference: [R^3::- $a $b]" puts "Cosine: [R^3::cos $c]" puts "3x3 matrices:" set A [R3x3::vector $a $b $c] set B [R3x3::vector $c $a $b] set C [R3x3::vector $b $c $a] puts "Sum: [R3x3::+ $A $B]" puts "Difference: [R3x3::- $A $B]" puts "Cosine: [R3x3::cos $C]"