Subject: A question for DEFUN

From: Chun-Yu Lee <[email-masked]>
Date: Mon, 9 May 94 17:24:38 MDT

Hello everybody,

I wrote a simple equation solver in LISP (see below). If a function
that uses the macro "equation" is defined before the file "eqsolver.lsp"
is loaded, everything seems all right. For example,

> (fmakunbound 'equation)
EQUATION
> (makunbound 'a)
A
> (makunbound 'b)
B
> (defun foo (a b) (equation a = b))
FOO
> (load "eqsolver.lsp")
;; Loading file EQSOLVER.LSP ...
;; Loading of file EQSOLVER.LSP is finished.
T
> (setq a nil b 5)
5
> (foo a b)
5 ;
A
> (foo nil b)
5 ;
A
> (equation a = b)
5 ;
A

However, when the function is defined after, then the result becomes as
follow:

> (load "eqsolver.lsp")
;; Loading file EQSOLVER.LSP ...
;; Loading of file EQSOLVER.LSP is finished.
T
> (makunbound 'a)
A
> (makunbound 'b)
B
> (defun foo (a b) (equation a = b))

*** - EVAL: variable B has no value     <--- Why does "defun" need to evaluate
1. Break>                                    the arguments of a function.

> (setq a nil b nil)
NIL
> (defun foo (a b) (equation a = b))
FOO             <--- O.K. But,
> (setq b 5)
5
> (foo a b)
NIL ;           <--- the output value is wrong.
A
> a
NIL
> b
5
> (equation a = b)
5 ;             <--- It's correct.
A

What's wrong with it? 
Thanks in advance.

Chun Yu Lee
e-mail: [email-masked]

=================File: EQSOLVER.LSP =============================

;;;
;;;  A Simple Equation Solver (SES)
;;;
;;; created: 18 April 1994
;;; updated: 29 April 1994
;;; author: Chun Yu Lee

  ; The following functions and macros serve to
  ; solve an equation that has less then one unknown and with three parts of
  ; expressions, the LHS, relationship, and RHS. No more than one unknown
  ; (variable) can be appeared in the LHS or RHS of the equation.
  ; A symbol name which is unbound or nil in the equation is an unknown.
  ; The operators or predicates used in expressions must be complyed with
  ; those specified in the exchanging rules depicted in global variable
  ; *X-operators*. However, any valid LISP forms other than specified in
  ; *X-operators* can be used in the expression which is on the opposite
  ; side to the one the unknown is stayed.
  ;
  ; The macro "symbol-equation" returns three values: the solved expression
  ; in symbolic form, the unknown variable, and the operater. But there is
  ; only one value will be returned, when the unknown cannot be found, to be
  ; true (T) or false (NIL) according to the evaluation of the equation.
  ;
  ; The macro "equation" returns two values: the evaluated result of solved
  ; equation and the unknown found. While the macro "nequation" is a
  ; destructive version of "equation" and returns only the evaluated result.
  ; The side effect of "nequation" is that the unbound (or nilled) variable
  ; will be bound to the result.
  ;
  ; SES can solve equations like followings:
  ;
  ;         a * b = c + 4  -- (1),
  ; or      b*x^2 + c = d  -- (2).
  ;
  ; Eqn. (1) can be called as:
  ;
  ;
  ; > (setq a 3 b 4)
  ; 4
  ; > (symbol-equation (* a b) = (+ c 4))
  ; (- (* A B) 4) ;
  ; C ;
  ; =
  ; > (equation (* a b) = (+ c 4))
  ; 8 ;
  ; C
  ; > (boundp 'c)
  ; NIL
  ; > (nequation (* a b) = (+ c 4))
  ; 8
  ; > c
  ; 8
  ;
  ; In the similar way, eqn. (2) can be called as:
  ;
  ; > (setq x nil b 3 c 4 d 5)
  ; 5
  ; > (symbol-equation (+ (* b (expt x 2)) c) = d)
  ; (EXPT (/ (- D C) B) (/ 1 2)) ;
  ; X ;
  ; =
  ; >
  ;

;;; Begining of the equation solver.
(defvar *X-operators*
  '((+ . -) (* . /) (expt . expt) (exp . log)
    (sin . asin) (cos . acos) (tan . atan)
    (sinh . asinh) (cosh . acosh) (tanh . atanh)
) )

(defmacro equation (&body eqn)
  (multiple-value-bind (value var) (eval (cons 'symbol-equation eqn))
    `(values ,(eval value) ',var)
) )

(defmacro nequation (&body eqn)  ;destructive version
  (multiple-value-bind (value var) (eval (cons 'symbol-equation eqn))
    (if var `(setq ,var ,(eval value)) value)
) )

(defmacro symbol-equation (lhs op rhs)
    (let* ((args (explode-list (list lhs rhs)))
           (var
            (nth
             (or (position-if-not
                  #'(lambda (x)
                      (or (numberp x) (boundp x) (fboundp x)))
                  args)
                 (position nil
                   (mapcar
                     #'(lambda (x)
                         (if (or (numberp x) (fboundp x)) x (eval x)))
                    args)
                 )
                 (return-from symbol-equation `(,op ,lhs ,rhs))
             )
           args)))
     `(values ',(solve-linear-eqn lhs rhs var) ',var ',op)
) )

(defun solve-linear-eqn (lhs rhs var)
  (if (or (and (atom rhs) (equal rhs var))
          (and (consp rhs) (member var (explode-list rhs))))
    (solve-linear-eqn-r rhs lhs var)
    (solve-linear-eqn-r lhs rhs var)
) )

(defun solve-linear-eqn-r (lhs rhs var)
  (if (atom lhs) (if (equal lhs var) rhs nil)
    (let ((op (pop lhs)) (result nil) lhs1)
      (push (or (cdr (assoc op *X-operators*))
                (car (rassoc op *X-operators*))) result)
      (push rhs result)
      (if (equal op 'expt) (setf (cadr lhs) `(/ 1 ,(cadr lhs))))
      (dolist (ivar lhs)
        (if (or (and (atom ivar) (equal ivar var))
                (and (consp ivar) (member var (explode-list ivar))))
            (setq lhs1 ivar)
            (push ivar result))
      )
      (solve-linear-eqn-r lhs1 (reverse result) var)
) ) )

(defun explode-list (lst)
  (if (null lst) nil
    (if (atom lst) (list lst)
      (append (explode-list (car lst)) (explode-list (cdr lst)))
) ) )

;;; End of equation solver.

---

From: [email-masked] (Joerg-Cyril Hoehle)
Date: Mon, 9 May 94 12:10:23 +0200

> is loaded, everything seems all right. For example,

Common-Lisp really needs all macros to be defined before they are
used. Not doing so in your first example makes Common-Lisp interpret
the call to EQUATION as a function call. That's not what seem to want.

[...]

 > 1. Break>                                    the arguments of a function.
It's not the defun, it's your macro that's calling EVAL several times.
The EQUATION macro can be expanded by Common-Lisp at the time the
defun is supplied, not necessarily when you call the new function.

[...]

 >   ; true (T) or false (NIL) according to the evaluation of the equation.

I believe that there is some misunderstanding here about the way
macros work. Macros can be used to transform an expression into
another expression, but this is usually only done once, for example
when you include your macro call in a defun. You _can_ write a macro
that analyses your equation and transforms it into another
expression. You _cannot_ write a macro that will solve your equation
in function of the unknown variable, because at macroexpansion time,
you don't know which variable is the unknown variable. You'll only
have this information at run-time (e.g. when you call FOO).

Reread the LISP FAQ: If you think that you need EVAL in a macro, then
you are almost certainly doing something wrong.

Why do you want to use macros anyway? To save you quotes? They are
only needed when testing your functions at top-level.

Attention! danger! In Common-Lisp, you'll have to understand and be
very careful about lexical and special variables in order to use
things like SETQ, SYMBOL-VALUE and BOUNDP to get the values of your
expressions or to manipulate values. I'd suggest using your own
binding list. This is easier to keep control of and doesn't interact
with others use of the variables.

Example:
(symbol-equation '((* a b) = (+ c 4)) '((a .3) (b . 4))) or
(symbol-equation '((* a b) = (+ c 4)) 'c)
(equation '((* a b) = (+ c 4)) '((a . 3) (b . 4))) or
(equation '((* a b) = (+ c 4)) 'c '((a . 3) (b . 4)))

 	Joerg Hoehle.
[email-masked]