Next: Defining New Simple Commands [Contents][Index]
The defmath
function (actually a Lisp macro) is like defun
except that code in the body of the definition can make use of the full
range of Calculator data types. The prefix ‘calcFunc-’ is added
to the specified name to get the actual Lisp function name. As a simple
example,
(defmath myfact (n) (if (> n 0) (* n (myfact (1- n))) 1))
This actually expands to the code,
(defun calcFunc-myfact (n) (if (math-posp n) (math-mul n (calcFunc-myfact (math-add n -1))) 1))
This function can be used in algebraic expressions, e.g., ‘myfact(5)’.
The ‘myfact’ function as it is defined above has the bug that an
expression ‘myfact(a+b)’ will be simplified to 1 because the
formula ‘a+b’ is not considered to be posp
. A robust
factorial function would be written along the following lines:
(defmath myfact (n) (if (> n 0) (* n (myfact (1- n))) (if (= n 0) 1 nil))) ; this could be simplified as: (and (= n 0) 1)
If a function returns nil
, it is left unsimplified by the Calculator
(except that its arguments will be simplified). Thus, ‘myfact(a+1+2)’
will be simplified to ‘myfact(a+3)’ but no further. Beware that every
time the Calculator reexamines this formula it will attempt to resimplify
it, so your function ought to detect the returning-nil
case as
efficiently as possible.
The following standard Lisp functions are treated by defmath
:
+
, -
, *
, /
, %
, ^
or
expt
, =
, <
, >
, <=
, >=
,
/=
, 1+
, 1-
, logand
, logior
, logxor
,
logandc2
, lognot
. Also, ~=
is an abbreviation for
math-nearly-equal
, which is useful in implementing Taylor series.
For other functions func, if a function by the name ‘calcFunc-func’ exists it is used, otherwise if a function by the name ‘math-func’ exists it is used, otherwise if func itself is defined as a function it is used, otherwise ‘calcFunc-func’ is used on the assumption that this is a to-be-defined math function. Also, if the function name is quoted as in ‘('integerp a)’ the function name is always used exactly as written (but not quoted).
Variable names have ‘var-’ prepended to them unless they appear in
the function’s argument list or in an enclosing let
, let*
,
for
, or foreach
form,
or their names already contain a ‘-’ character. Thus a reference to
‘foo’ is the same as a reference to ‘var-foo’.
A few other Lisp extensions are available in defmath
definitions:
elt
function accepts any number of index variables.
Note that Calc vectors are stored as Lisp lists whose first
element is the symbol vec
; thus, ‘(elt v 2)’ yields
the second element of vector v
, and ‘(elt m i j)’
yields one element of a Calc matrix.
setq
function has been extended to act like the Common
Lisp setf
function. (The name setf
is recognized as
a synonym of setq
.) Specifically, the first argument of
setq
can be an nth
, elt
, car
, or cdr
form,
in which case the effect is to store into the specified
element of a list. Thus, ‘(setq (elt m i j) x)’ stores ‘x’
into one element of a matrix.
for
looping construct is available. For example,
‘(for ((i 0 10)) body)’ executes body
once for each
binding of ‘i’ from zero to 10. This is like a let
form in that ‘i’ is temporarily bound to the loop count
without disturbing its value outside the for
construct.
Nested loops, as in ‘(for ((i 0 10) (j 0 (1- i) 2)) body)’,
are also available. For each value of ‘i’ from zero to 10,
‘j’ counts from 0 to ‘i-1’ in steps of two. Note that
for
has the same general outline as let*
, except
that each element of the header is a list of three or four
things, not just two.
foreach
construct loops over elements of a list.
For example, ‘(foreach ((x (cdr v))) body)’ executes
body
with ‘x’ bound to each element of Calc vector
‘v’ in turn. The purpose of cdr
here is to skip over
the initial vec
symbol in the vector.
break
function breaks out of the innermost enclosing
while
, for
, or foreach
loop. If given a
value, as in ‘(break x)’, this value is returned by the
loop. (Lisp loops otherwise always return nil
.)
return
function prematurely returns from the enclosing
function. For example, ‘(return (+ x y))’ returns ‘x+y’
as the value of a function. You can use return
anywhere
inside the body of the function.
Non-integer numbers cannot be included
directly into a defmath
definition. This is because the Lisp
reader will fail to parse them long before defmath
ever gets control.
Instead, use the notation, ‘:"3.1415"’. In fact, any algebraic
formula can go between the quotes. For example,
(defmath sqexp (x) ; sqexp(x) == sqrt(exp(x)) == exp(x*0.5) (and (numberp x) (exp :"x * 0.5")))
expands to
(defun calcFunc-sqexp (x) (and (math-numberp x) (calcFunc-exp (math-mul x '(float 5 -1)))))
Note the use of numberp
as a guard to ensure that the argument is
a number first, returning nil
if not. The exponential function
could itself have been included in the expression, if we had preferred:
‘:"exp(x * 0.5)"’. As another example, the multiplication-and-recursion
step of myfact
could have been written
:"n * myfact(n-1)"
A good place to put your defmath
commands is your Calc init file
(the file given by calc-settings-file
, typically
~/.emacs.d/calc.el), which will not be loaded until Calc starts.
If a file named .emacs exists in your home directory, Emacs reads
and executes the Lisp forms in this file as it starts up. While it may
seem reasonable to put your favorite defmath
commands there,
this has the unfortunate side-effect that parts of the Calculator must be
loaded in to process the defmath
commands whether or not you will
actually use the Calculator! If you want to put the defmath
commands there (for example, if you redefine calc-settings-file
to be .emacs), a better effect can be had by writing
(put 'calc-define 'thing '(progn (defmath ... ) (defmath ... ) ))
The put
function adds a property to a symbol. Each Lisp
symbol has a list of properties associated with it. Here we add a
property with a name of thing
and a ‘(progn ...)’ form as
its value. When Calc starts up, and at the start of every Calc command,
the property list for the symbol calc-define
is checked and the
values of any properties found are evaluated as Lisp forms. The
properties are removed as they are evaluated. The property names
(like thing
) are not used; you should choose something like the
name of your project so as not to conflict with other properties.
The net effect is that you can put the above code in your .emacs file and it will not be executed until Calc is loaded. Or, you can put that same code in another file which you load by hand either before or after Calc itself is loaded.
The properties of calc-define
are evaluated in the same order
that they were added. They can assume that the Calc modules calc.el,
calc-ext.el, and calc-macs.el have been fully loaded, and
that the *Calculator* buffer will be the current buffer.
If your calc-define
property only defines algebraic functions,
you can be sure that it will have been evaluated before Calc tries to
call your function, even if the file defining the property is loaded
after Calc is loaded. But if the property defines commands or key
sequences, it may not be evaluated soon enough. (Suppose it defines the
new command tweak-calc
; the user can load your file, then type
M-x tweak-calc before Calc has had chance to do anything.) To
protect against this situation, you can put
(run-hooks 'calc-check-defines)
at the end of your file. The calc-check-defines
function is what
looks for and evaluates properties on calc-define
; run-hooks
has the advantage that it is quietly ignored if calc-check-defines
is not yet defined because Calc has not yet been loaded.
Examples of things that ought to be enclosed in a calc-define
property are defmath
calls, define-key
calls that modify
the Calc key map, and any calls that redefine things defined inside Calc.
Ordinary defun
s need not be enclosed with calc-define
.
Next: Defining New Simple Commands [Contents][Index]