Operations on series of numerical data

Arjen Markus (31 march 2014) This may be the start of a new package, one to manipulate series of numerical data at a high level in pure Tcl. The code below is a humble experiment. Of course for large amounts of data it would be better to have a compiled extension and there are certainly packages around or in development that can/will do that, but it is also attractive to have a Tcl-only package.

# series.tcl --
#      First attempt at creating a package to handle series of numerical data
#
namespace eval ::math::series {
}

# mexpr --
#     Evaluate an expression with lists (series) of data
#
# Arguments:
#     expression       String valid as a Tcl expression
#
# Returns:
#     List of the results of the expression evaluated with subsequent values
#
# Note:
#     Array elements $x($y) in these expressions do not yet work properly!
#     Also avoid variables whose name has the form "__...___"
#
#     The current implementation may not be very efficient, because of
#     the expression being reparsed each iteration.
#
proc ::math::series::mexpr {expression} {

    #
    # Extract the variable names
    #
    set vars [lsort -unique [regexp -all -inline {\$[A-Za-z_]+} $expression]]

    set forvars {}
    foreach v $vars {
        set v [string range $v 1 end]
        upvar 1 $v __${v}__
        if { [llength [set __${v}__]] > 1 } {
            lappend forvars $v [set __${v}__]
        } else {
            set $v [set __${v}__]
        }
    }

    #
    # Evaluate the expression via the list variables
    #
    if { [llength $forvars] > 0 } {
        set __result__ {}
        foreach {*}$forvars {
            lappend __result__ [expr $expression]
        }
    } else {
        set __result__ [expr $expression]
    }

    return $__result__
}

#
# procedure to compare the performance
#
proc test_expr {xlist ylist} {
    foreach x $xlist y $ylist {
        lappend result [expr {$x+$y}]
    }
    return $result
}

#
# Simple test
#
set x {1 2 3 4 5 6 7 8 9}
set y {2 3 4 5 6 7 8 9 10}
set z 1


puts [::math::series::mexpr {$x+$y}]
puts [::math::series::mexpr {$x+$z}]
puts [test_expr $x $y]
puts [::math::series::mexpr {1+2}]

set x {}
set y {}
for {set i 0} {$i < 1000} {incr i} {
    lappend x $i
    lappend y $i
}

# Hm, mexpr is about 4 times as slow as test_expr
puts "Time for mexpr:     [time {::math::series::mexpr {$x+$y}} 1000]"
puts "Time for test_expr: [time {test_expr $x $y} 1000]"