calc: Defining Functions

 
 18.5.1 Defining New Functions
 -----------------------------
 
 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:
 
    • The ‘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.
 
    • The ‘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.
 
    • A ‘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.
 
    • The ‘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.
 
    • The ‘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’.)
 
    • The ‘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 (and extremely large integers) 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’.