tcl::mathfunc

The ::tcl::mathfunc namespace provides commands for all the functions available in [expr]

See Also

Documentation

official reference
TIP 232
for the creation of the ::tcl::mathfunc namespace
TIP 174
Math Operators as Commands. Presents various usage ideas.

Description

::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.

List of built-in functions

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

User-Defined Functions

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.

Defining New Functions at the C Level

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);

Namespace-local tcllib::mathfun

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)}
    }
}

func: a Convenient Procedure

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

Using Math Functions in a Namespace

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.

atan2

AMG: The man page for atan2 says that its arguments can't both be zero [1 ]. 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?

Non-Math mathfunc

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
    }
}