tcl::mathfunc

Difference between version 39 and 40 - Previous - Next
The '''::tcl::mathfunc''' [namespace] provides commands for all the functions
available in [[`[expr]`] 

<<TOC>>



** See Also **

    * [Additional math functions]



** Documentation ** 

   [http://www.tcl.tk/man/tcl/TclCmd/mathfunc.htm%|%official reference]:   

   [TIP] [http://tip.tcl.tk/232%|%232]:   for the creation of the '''::[tcl::mathfunc]''' [namespace]

   [TIP] [http://www.tcl.tk/cgi-bin/tct/tip/174.html%|%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 function%|%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] [http://tip.tcl.tk/232%|%232] provides for script-level creation of new
functions, analagous to the [C] level
[http://www.tcl.tk/man/tcl8.5/TclLib/CrtMathFnc.htm%|%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 [proc]s
and [command]s.  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 [comp.lang.tcl%|%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.

======c
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...

======c
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:

[http://groups.google.com/group/comp.lang.tcl/msg/f868deda4d3905ac%|%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
[http://www.tcl.tk/man/tcl8.6/TclCmd/mathfunc.htm#M10].  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
    }
}
======
** Not only scalars **

[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})}]
======

<<categories>> Command | Syntax | Mathematics