[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 ====== What is missing, still, is a more detailed definition of the input and output parameters, that is what is the size of x and the size of y. That may lead to different behavior of the component with respect to the numerical method applied, or how the function can be plot. The following is a brief sketch for such a class, as an extension to your example. ====== ::snit::type function { option -function option -inputnb -default 1 option -outputnb -default 1 method value {x} { set cmd $options(-function) if {$cmd==""} then { error "Option -function is mandatory." } lappend cmd $x return [eval $cmd] } method minimum {a b} { if {$options(-outputnb)!=1} then { error "Multi-objective optimization is not available" } elseif {$options(-inputnb)==1} then { set result [::math::optimize::minimum $a $b $self] } else { # More complex optimization, for example a fictive SQP method set sqp [sqp create %AUTO%] $sqp configure -nbparams $options(-inputnb) $sqp configure -function $options(-function) set result [$sqp find] $sqp destroy } return $result } } ====== <> Snit | Mathematics