The ::tcl::mathfunc namespace provides commands for all the functions available in [expr]
::tcl::mathfunc was made available in Tcl 8.5. Operators are in tcl::mathop. Note that changing or adding to these commands changes the set of functions available in expr. Also note that when your code is executing in a namespace other than the global one, you can define your own functions in the "yourNS::tcl::mathfunc" namespace.
Operation | Name | Args | Operation | Name | Args | |
---|---|---|---|---|---|---|
Absolute value | abs | arg | Hypotenuse length | hypot | x y | |
Arc cosine | acos | arg | Coerce to word-sized integer | int | arg | |
Arc sine | asin | arg | Natural logarithm | log | arg | |
Arc tangent | atan | arg | Base-10 logarithm | log10 | arg | |
Four-quadrant arc tangent | atan2 | y x | Greatest value | max | args | |
Coerce to boolean | bool | arg | Least value | min | args | |
Round up to whole number | ceil | arg | Power | pow | x y | |
Cosine | cos | arg | Random float in range [0,1) | rand | ||
Hyberbolic cosine | cosh | arg | Round to whole number | round | arg | |
Coerce to float | double | arg | Sine | sin | arg | |
Coerce to integer | entier | arg | Hyperbolic sine | sinh | arg | |
Exponential | exp | arg | Square root | sqrt | arg | |
Round down to whole number | floor | arg | Seed random number generator | srand | arg | |
Remainder | fmod | x y | Tangent | tan | arg | |
Coerce to 64-bit integer | wide | arg | Hyperbolic tangent | tanh | arg | |
Integer part of square root | isqrt | arg |
TIP 232 provides for script-level creation of new functions, analagous to the C level Tcl_CreateMathFunc(3) .
[expr] can be extended with new functions by adding commands to ::tcl::mathfunc, or to a tcl::matchfunc child namespace of any other namespace. In the latter case, the additional [expr] functions are only available to commands in the parent namespace.
Functions can also be overriden or deleted from [expr] by overriding or modifying the corresponding procedure in an appropriate tcl::mathfunc namespace. This functionality was previously not available, even at the C level.
[expr] function arguments are now just as flexible as those of Tcl procs and commands. Variadic numbers of arguments are also possible, and illustrated by by the min() and max() functions.
[expr] functions can now also be called as procedures without using [expr] at all. This is one way to to bypassing the problems discussed at brace your expr-essions.
AM: On the c.l.t the other day [is this as of May 2003?], Martin Russell asked about how to define new math functions. If you want to do it without the help of DKF's extension [??] and CrtLib [ critcl's critlib?], then here is a recipe provided by Pat Thoyts:
Something along these lines.
static Tcl_MathProc ArbLogProc; static int ArbLogProc(clientData, interp, args, resultPtr) ClientData clientData; Tcl_Interp *interp; /* current interpreter */ Tcl_Value *args; /* input arguments */ Tcl_Value *resultPtr; /* where to store the result */ { double b, n, d; b = args[0].doubleValue; n = args[1].doubleValue; /* do your maths and assign d to the result */ d = 1.0; resultPtr->type = TCL_DOUBLE; resultPtr->doubleValue = d; return TCL_OK; }
in your package initialisation...
Tcl_ValueType arblogArgs[2] = { TCL_DOUBLE, TCL_DOUBLE }; Tcl_CreateMathFunc(interp, "arblog", 2, arblogArgs, ArbLogProc, (ClientData)NULL);
As of Tcl 8.5, if any namespace has a child namespace called tcl::mathfunc (notice that it's a relative name), [expr] will look first in that namespace for available functions. I.e., either ::tcl::matfunc::f or [namespace current]::tcl::mathfunc::f can resolve f($x).
In the following example, any proc in mynamespace can use logbase():
namespace eval mynamespace { proc tcl::mathfunc::logbase {x b} { expr {log($x) / log($b)} } proc dostuff {} { ... expr {logbase($some_var,$some_other_var)} } }
DKF: A little helper for when writing custom functions:
proc func {name arguments body} { proc tcl::mathfunc::$name $arguments [list expr $body] }
This lets us write factorials as just this:
func fac x { $x<2 ? 1 : $x*fac($x-1) }
It can be useful to import functions from elsewhere into the local tcl::mathfunc namespace:
namespace eval tcl::mathfunc { namespace import ::otherns::* }
References:
tcl talked badly (and high on reddit) ,comp.lang.tcl ,2008-02-15
DKF: If you like doing your computations the Lisp way, add this to your scripts:
namespace path {::tcl::mathop ::tcl::mathfunc}
Now you can use all the above functions (and the math operators) as commands without qualification.
AMG: The man page for atan2 says that its arguments can't both be zero [L1 ]. On one system I checked, expr atan2(0,0) returns 0.0. On other systems, I suspect it may behave differently. Right? If so, shouldn't the implementation of atan2 check if both arguments are zero and throw an error?
bsg: Non-math mathfunc: who says all mathfunc's have to be about math. Turning common tcl commands into functions can clean up conditional statements, making them easier to read.
Turn something like this:
if {[lindex $data [expr {2*$i +1}]] eq [lindex $items [expr {2*$itemno +1}]]} {break}
into this:
if {lindex($data, (2*$i+1)) eq lindex($items, (2*$itemno +1))} {break}
Another example:
if { $priv(mBar) eq [string range $menu 0 [expr {[string length $priv(mBar)] - 1}]]} { ... } if { $priv(mBar) eq substr($menu, 0, (strlen($priv(mBar)) - 1))} { ... }
Notice how it also eliminates the recursive expr calls.
Here's what I've found useful so far:
namespace eval tcl::mathfunc { proc strlen {str} { ::string length $str } proc stridx {str index} { ::string index $str $index } proc substr {str first last} { ::string range $str $first $last } proc llength {list} { ::llength $list } proc lindex {list index args} { ::lindex $list $index {*}$args } proc lrange {list first last} { ::lrange $list $first $last } }
arjen: Mathematical functions defined this way can take lists as arguments and produce lists as results, as illustrated by these rather trivial examples:
proc tcl::mathfunc::maxlist {values} { set maxvalue [lindex $values 0] foreach value $values { set maxvalue [expr {$value > $maxvalue? $value : $maxvalue}] } return $maxvalue } proc tcl::mathfunc::multiply {factor values} { set result {} foreach value $values { lappend result [expr {$factor * $value}] } return $result } puts [expr {maxlist({4 1 2 3})}] puts [expr {multiply(2,{4 1 2 3})}]