Arjen Markus (21 october 2004) I was facing the problem of having to read a file with a lot of floating-point data. The file contained a large set of timeseries (organised as data per time) and I needed to determine the average of each timeseries. So, in a similar fashion as Numerical array operations, I thought it should be possible to do this in a "generic" way:
Loop over all the records of the file: Sum the data per time Divide all the sums by the number of times Output
Now, in Playing APL RS showed how to do this with (in principle) a sequence or array of arbitrary dimensions (in his case: using newlines to separate the levels). I wanted a bit more flexibility and performance though.
Here is my experiment:
Missing from this:
For instance:
The binary operations just combine the individual elements:
{1 2 3 4 5 6} + {1 2 3 4 5 6} ==> {2 4 6 8 10 12}
# arrayop.tcl -- # Element-wise array operations (arrays are implemented as # lists or lists of lists, to any depth needed) # # Note: # Because the various operations require very similar code # to handle arbitrary dimensions, I use a template to generate # the actual code "on the fly" # namespace eval ::Arrays { # Create the namespace set CODE { # Lscalar_<op> -- # Perform a binary operation on a scalar and an array # Arguments: # op1 Scalar in question # op2 Array (nested list) # Result: # Array with updated values # proc ::Arrays::Lscalar_<op> {op1 op2} { set v $op1 if { [llength [lindex $op2 0]] <= 1 } { set result {} foreach w $op2 { lappend result [<expr>] } } else { set result {} foreach w $op2 { lappend result [Lscalar_<op> $v $w] } } return $result } # Rscalar_<op> -- # Perform a binary operation on an array and a scalar # Arguments: # op1 Array (nested list) # op2 Scalar in question # Result: # Array with updated values # proc ::Arrays::Rscalar_<op> {op1 op2} { set w $op2 if { [llength [lindex $op1 0]] <= 1 } { set result {} foreach v $op1 { lappend result [<expr>] } } else { set result {} foreach v $op1 { lappend result [Rscalar_<op> $v $w] } } return $result } # Array_<op> -- # Perform a binary operation on two arrays # Arguments: # op1 First array (nested list) # op2 Second array (nested list) # Result: # Array with updated values # proc ::Arrays::Array_<op> {op1 op2} { if { [llength [lindex $op1 0]] <= 1 } { set result {} foreach v $op1 w $op2 { lappend result [<expr>] } } else { set result {} foreach v $op1 w $op2 { lappend result [Array_<op> $v $w] } } return $result } # <op> -- # Perform a binary operation on two arrays or scalars # Arguments: # op1 First array (nested list) or scalar # op2 Second array (nested list) or scalar # Result: # Array with updated values # proc ::Arrays::<op> {op1 op2} { if { [llength $op1] <= 1 } { return [::Arrays::Lscalar_<op> $op1 $op2] } if { [llength $op2] <= 1 } { return [::Arrays::Rscalar_<op> $op1 $op2] } else { return [::Arrays::Array_<op> $op1 $op2] } } # # End of template } # add, sub, mult, div, max, min -- # Binary operations on arrays # eval [string map {<op> add <expr> "expr {$v+$w}"} $CODE] eval [string map {<op> sub <expr> "expr {$v-$w}"} $CODE] eval [string map {<op> mult <expr> "expr {$v*$w}"} $CODE] eval [string map {<op> div <expr> "expr {$v/$w}"} $CODE] eval [string map {<op> max <expr> "expr {$v>$w?$v:$w}"} $CODE] eval [string map {<op> min <expr> "expr {$v<$w?$v:$w}"} $CODE] } ;# End of namespace # mkArray -- # Make an array with a uniform value # Arguments: # ndim "Toplevel" dimension # args Inferior dimensions and the value to be assigned # Result: # Array with updated values # proc ::Arrays::mkArray {ndim args} { puts "mkArray: $ndim -- $args" if { [llength $args] < 1 } { error "At least two arguments required - dimension and value" } if { [llength $args] == 1 } { set v [lindex $args 0] } else { set v [eval mkArray $args] } set result {} set i $ndim while { $i > 0 } { lappend result $v incr i -1 } return $result } # test -- # # Test the array construction # console show puts "2x3 array of 1's: [::Arrays::mkArray 2 3 1]" puts "5x3x2 array of x's: [::Arrays::mkArray 5 3 2 x]" set array1 { {1 2 3} {4 5 6} } set array2 { {6 5 4} {3 2 1} } set array3 { { {6 5 4} {3 2 1} } { {6 5 4} {3 2 1} } } set array4 { { {1 2 3} {4 5 6} } { {1 2 3} {4 5 6} } } puts "Sum: $array1+$array2 =" puts " [::Arrays::add $array1 $array2]" puts "Sum: $array3+$array4 =" puts " [::Arrays::add $array3 $array4]" puts "Min: min($array3,$array4) =" puts " [::Arrays::min $array3 $array4]" puts "Min: min($array3,4) =" puts " [::Arrays::min $array3 4]"