Version 4 of Mathematical functions and Snit

Updated 2008-11-14 21:53:03 by MB

Arjen Markus (11 november 2008) Here is an experiment with Snit where I want to provide a set of methods to examine mathematical functions. It is a fairly stragihtforward exercise in wrapping procedures from Tcllib and Tklib.

One particular aspect is noteworthy: in interactive use, it would be nice to invoke the function as [f 1.0], rather than [f value 1.0], as the first form is more concise and seems more natural. You need to supply an implicit method name then and this is achieved by the UnknownMethod method - the delegation mechanism in Snit works very nice.


# function_obj.tcl --
#     Mathematical functions as a Snit object
#

package require snit
package require math::calculus
package require math::optimize

# function --
#     Define the "function" type
#
::snit::type function {
    typevariable top 0
    delegate method * using {%s UnknownMethod %m}
    option -steps -default 100

    constructor {arglist body args} {
	proc ${selfns}::F $arglist $body
	$self configurelist $args
    }

    method value {x} {
	${selfns}::F $x
    }

    method integral {a b} {
    #
    # Romberg returns the value and an error estimate. Just return the
    # value of the integral.
    #
	lindex [::math::calculus::romberg [list $self value] $a $b] 0
    }

    method table {a b} {
	set dx [expr {($b-$a)/double($options(-steps))}]
	set table {}
	for {set i 0} {$i <= $options(-steps)} {incr i} {
	    set x [expr {$a + $i*$dx}]
	    if { abs($x) < 0.5*$dx } {
		set x 0.0   ;# Make sure x obtains the exact value zero,
			     # if required
	    }
	    set r [$self value $x]
	    lappend table [list $x $r]
	}
	return $table
    }

    method print {a b {format {%10.4f %10.4f}}} {
	set table [$self table $a $b]
	set result ""

	foreach row $table {
	    foreach {x r} $row {break}
	    append result "[format $format $x $r]\n"
	}
	return $result
    }

    method zero {a b} {
	return [::math::calculus::regula_falsi $self $a $b]
    }

    method minimum {a b} {
	return [::math::optimize::minimum $a $b $self]
    }

    method maximum {a b} {
	return [::math::optimize::maximum $a $b $self]
    }

    #
    # Not finished yet:
    # TODO: scaling, determining the right widget
    #
    method plot {a b} {
	package require Tk
	package require Plotchart

	set table [$self table $a $b]

	set ymin {}
	set ymax {}
	foreach row $table {
	    foreach {x y} $row {break}
	    if { $ymin == {} || $ymin > $y } { set ymin $y }
	    if { $ymax == {} || $ymax < $y } { set ymax $y }
	}

	incr top
	toplevel .plot$top
	pack [canvas .plot$top.c]
	set p [::Plotchart::createXYPlot .plot$top.c \
		  [::Plotchart::determineScale $a $b] \
		  [::Plotchart::determineScale $ymin $ymax]]

	foreach row $table {
	    foreach {x y} $row {break}
	    $p plot graph $x $y
	}
    }

    #
    # For interactive use, the abbreviation "f 1.0" instead of
    # "f value 1.0" would be very useful ...
    #
    method UnknownMethod {subcommand args} {
	if {[string is double -strict $subcommand]} {
	    ${selfns}::F $subcommand
	} else {
	    error "Unknown subcommand: $subcommand"
	}
    }

    # More to come:
    # - view, deriv, create-deriv, create-integral
}

# main --
#     Testing this
#

function f {x} {expr {$x==0.0? 1.0 : sin($x)/$x}}

puts "Value at x=0: [f 0.0]"
#catch {puts [f 0.0]} msg;puts $msg; puts $errorInfo
# Alternative: [f value 1.0]

puts "Integral from 0 to pi: [f integral 0 [expr {acos(-1.)}]]"
puts "Zero between 0 and 4: [f zero 0 4]"
puts "Minimum between 0 and 10: [f minimum 0 10]"
puts "Maximum between 0 and 10: [f maximum 0 10]"
f plot -10 10

f configure -steps 20
puts [f print 0 8] 

MB: The core of your idea is to define the command in the constructor, which leads to the very natural definition of the function f, which in fact creates the object f as an instance of the class function. This is a clever use of SNIT, indeed. My simpler idea for that problem would have been based on a callback instead, with a client such as :

proc myfunc {x} {expr {$x==0.0? 1.0 : sin($x)/$x}}
function f
f configure -function myfunc