From: Drew Krause
Subject: macros & loops *blech*
Date: 
Message-ID: <DmWte.8988$pa3.8652@newsread2.news.atl.earthlink.net>
Hello all you patient Lisp gurus out there,

I'm starting to think that maybe I don't understand macros very well ;) 
My task is to automate the construction of difference equations, so that 
after creating one, I can plug in a number & it will return a value.

Briefly, a difference equation (as I'm using it) consists of:

-- initial variables for 0, 1, etc. (the number of init values depends 
on the equation)
-- an equation for returning a value based on the number itself and the 
preceding values returned
-- (my invention) a final transformation of the sum of the values 
computed above

I've been able to construct functions by expanding, so that calling

(make-diff 'n                                 ; variable name
               'dsksdiff                       ; function name
             '(1 2)                            ; init values
             '((lambda (x) (mod (* x x) 5))    ; list of rules
               (lambda (x) (mod (* -1 x) 7)))
             '(lambda (x) (mod x 12))))        ; final transformation

will give me what I want, ie the function:

(COND ((= N 0) 1)
        ((= N 1) 2)
        (T
         ((LAMBDA (X) (MOD X 12))
          (+ ((LAMBDA (X) (MOD (* X X) 5)) N)
             ((LAMBDA (X) (MOD (* -1 X) 7)) (DSKDIFF (- N 1))))))))

Now, here's where it gets weird ......
If I copy & paste the above expansion and name it as a defun, I can use 
loops:

(defun dskdiff (n)
  (COND ((= N 0) 1)
        ((= N 1) 2)
        (T
         ((LAMBDA (X) (MOD X 12))
          (+ ((LAMBDA (X) (MOD (* X X) 5)) N)
             ((LAMBDA (X) (MOD (* -1 X) 7)) (DSKDIFF (- N 1))))))))

(loop for x to 8 collect (dskdiff x)) ; = (1 2 9 9 6 1 7 4 7)

....but if I try to call it a macro, it has no problem returning single 
values, but blanches at loops:

(defmacro drewsdiff (n)
  (make-diff 'n 'drewsdiff
             '(1 2)
             '((lambda (x) (mod (* x x) 5))
               (lambda (x) (mod (* -1 x) 7)))
             '(lambda (x) (mod x 12))))

(drewsdiff 8) ; = 7
(loop for x to 8 collect (drewsdiff x)) ; *** - Lisp stack overflow.

.... Am I missing something here? Thanks for any help! Gory details 
below. Drew

;; MAKE-DIFF-INITS -- sets all inits for a diff equation
(defun make-diff-inits (varname initlist)
  (loop for r to (- (length initlist) 1) collect
        `((= ,varname ,r) ,(nth r initlist))))

;; MAKE-DIFF-FUNC -- sets the 'else' clause for a diff equation
;; vname = variable name
;; fname = function name
;; funclist = lambda functions for n, n-1, n-2 etc.
;; resultfunc = global lambda, applied after sum is computed
(defun make-diff-func (vname fname funclist
                             &optional (resultfunc '(lambda (x) x)))
  (let ((funcsum      (append (list '+)
      (list (list (first funclist) vname))
             (loop for y from 1 to (- (length funclist) 1) collect
                   (list (nth y funclist) `(,fname (- ,vname ,y)))))))
  `(t (,resultfunc ,funcsum))))

;; MAKE-DIFF combining inits & functions into a diff equation
(defun make-diff (vname fname inits funclist
                                 &optional (resultfunc '(lambda (x) x)))
  (append (list 'cond) (make-diff-inits vname inits)
          (list (make-diff-func vname fname funclist resultfunc))))

From: Kaz Kylheku
Subject: Re: macros & loops *blech*
Date: 
Message-ID: <1119369731.496896.271980@z14g2000cwz.googlegroups.com>
Drew Krause wrote:
> ....but if I try to call it a macro, it has no problem returning single
> values, but blanches at loops:
>
> (defmacro drewsdiff (n)
>   (make-diff 'n 'drewsdiff
>              '(1 2)
>              '((lambda (x) (mod (* x x) 5))
>                (lambda (x) (mod (* -1 x) 7)))
>              '(lambda (x) (mod x 12))))
>
> (drewsdiff 8) ; = 7
> (loop for x to 8 collect (drewsdiff x)) ; *** - Lisp stack overflow.

Your macro expands to a call into itself. The MAKE-DIFF function
computes a form which contains a (DREWSDIFF ...) call. The form put out
by a macro is subject to more macroexpansion, so what you have here is
a recursive macro.

Recursive macros are fine as long as they terminate. Or, I should say,
as long as they terminate in a reasonable number of stack frames and
memory usage. This means that at some point, one of the recursive
invocations of DREWSDIFF has to somehow make a decision to put out a
form which no longer incorporates another call to DREWSDIFF.

Maybe you want to choose a different name for the macro? The function
could be called MAKE-DIFF-HELPER and the macro MAKE-DIFF, perhaps.

Also, if you are going to be calling functions out of macros, you
better have this:

 (eval-when (:compile-toplevel :load-toplevel :execute)
   (defun function-used-by-macro ...) ...)

Otherwise, when you are compiling the code, the function definitions
will not be elaborated within the compiler, and the macros will be
making calls to nonexistent functions! (The compiler's job is to
translate functions, not to actually make them available for execution
within its own image --- unless you tell it to with EVAL-WHEN).
From: Alan Crowe
Subject: Re: macros & loops *blech*
Date: 
Message-ID: <86k6kn5zvm.fsf@cawtech.freeserve.co.uk>
Drew Krause wrote:
     (defmacro drewsdiff (n)
       (make-diff 'n 'drewsdiff
		  '(1 2)
		  '((lambda (x) (mod (* x x) 5))
		    (lambda (x) (mod (* -1 x) 7)))
		  '(lambda (x) (mod x 12))))

     (drewsdiff 8) ; = 7
     (loop for x to 8 collect (drewsdiff x)) ; *** - Lisp stack overflow.

     .... Am I missing something here? Thanks for any help! Gory details 
     below. Drew

I could not reproduce this. (drewsdiff 8) went into an
infinite loop. Looking at the macroexpansion

* (macroexpand-1 '(drewsdiff 8))

(COND ((= N 0) 1)
      ((= N 1) 2)
      (T
       ((LAMBDA (X) (MOD X 12))
        (+ ((LAMBDA (X) (MOD (* X X) 5)) N)
           ((LAMBDA (X) (MOD (* -1 X) 7)) (DREWSDIFF (- N 1)))))))

we see that it calls itself at the end, so macroexpansion
will never terminate.

I think you've bitten off rather a lot for a first go at
writing a macro that writes a function.

Begin with x_n = x_{n-1} + x_{n-2}. 
We want to generate

(defun /name/ (n)
  (case n
	(0 /something/)
	(1 /something-else/)
	(t (+ (name (- n 1))
	      (name (- n 2))))))

We can write

(defmacro defdif (name x0 x1)
  `(defun ,name (n)
     (case n
	   (0 ,x0)
	   (1 ,x1)
	   (t (+ (,name (- n 1))
		 (,name (- n 2)))))))

(defdif fibonacci 0 1) => FIBONACCI
(fibonacci 4) => 3

This code has problems. x0 and x1 get pasted directly into
the macroexpansion so they get executed everytime the
recursion hits its base case.

* (defdif fibonacci (print 0) (print 1))

FIBONACCI
* (fibonacci 4)

1 
0 
1 
1 
0 
3

Also the binding of the formal parameter, n, gets between
the forms x0 and x1 and their lexical environment.

Since this way of calculating a difference equation is a tree recursion it is
hopelessly inefficient. 

On the other hand this stuff is fun to play with, so lets
not get too po-faced about it :-)

A rather important point is that this can be done quite naturally without
using a macro

* (defun diff-eqn (n initial-conditions weighting-functions)
    (assert (= (length initial-conditions)
	       (length weighting-functions)))
    (let ((order (length initial-conditions)))
      (if (< n order)
	  ;; if n is small we can just look up the
	  ;; appropriate initial condition
	  (elt initial-conditions n)
	(loop for r below order
	      ;; if n is larger we must sum the contributions
	      ;; from recursive calls to diff-eqn with
	      ;; smaller n
	      sum (funcall (elt weighting-functions r)
			   (diff-eqn (- n r 1)
				     initial-conditions
				     weighting-functions))))))

* (dotimes (i 10)
    (format t "~A ~A~%"
	    i
	    (diff-eqn i
		      (list 1 1)
		      (list #'(lambda (x) x)
			    #'(lambda (x) x)))))
0 1
1 1
2 2
3 3
4 5
5 8
6 13
7 21
8 34
9 55
NIL

Macros become especially difficult to understand when they
are not really needed.

Alan Crowe
Edinburgh
Scotland
From: Drew Krause
Subject: Re: macros & loops *blech*
Date: 
Message-ID: <Td%te.9116$pa3.8996@newsread2.news.atl.earthlink.net>
Thanks for your replies. Actually, what I didn't express very well was 
that I indeed wanted to set up a difference-equation *function* that 
behaves like this:

.... set up a difference equation ....
(define mydiff (make-diff buncha-parameters))

...then use it to return values, eg. ...
(mydiff 10)         ; = 99
(loop for x from 13 to 16 collect (mydiff x))     ;  =  (55 -1 6 91)

So a macro *was* needed. Bob Coyne of the Lisp Musig helped out with a 
solution. I'm forwarding it to the list:

(defmacro define-diff (vname fname inits funclist
			  &optional (resultfunc '(lambda (x) x)))
   `(defun ,fname (,vname)
      ,(make-diff vname fname inits funclist resultfunc)))

(define-diff n
   dskdiff	
   (1 2)	
   ((lambda (x) (mod (* x x) 5))		; list of rules
    (lambda (x) (mod (* -1 x) 7)))
   (lambda (x) (mod x 12)))

CL-USER> (loop for i from 0 to 10 collect (dskdiff i))
(1 2 9 9 6 1 7 4 7 1 6)


Drew Krause wrote:
> Hello all you patient Lisp gurus out there,
> 
> I'm starting to think that maybe I don't understand macros very well ;) 
> My task is to automate the construction of difference equations, so that 
> after creating one, I can plug in a number & it will return a value.
> 
> Briefly, a difference equation (as I'm using it) consists of:
> 
> -- initial variables for 0, 1, etc. (the number of init values depends 
> on the equation)
> -- an equation for returning a value based on the number itself and the 
> preceding values returned
> -- (my invention) a final transformation of the sum of the values 
> computed above
> 
> I've been able to construct functions by expanding, so that calling
> 
> (make-diff 'n                                 ; variable name
>               'dsksdiff                       ; function name
>             '(1 2)                            ; init values
>             '((lambda (x) (mod (* x x) 5))    ; list of rules
>               (lambda (x) (mod (* -1 x) 7)))
>             '(lambda (x) (mod x 12))))        ; final transformation
> 
> will give me what I want, ie the function:
> 
> (COND ((= N 0) 1)
>        ((= N 1) 2)
>        (T
>         ((LAMBDA (X) (MOD X 12))
>          (+ ((LAMBDA (X) (MOD (* X X) 5)) N)
>             ((LAMBDA (X) (MOD (* -1 X) 7)) (DSKDIFF (- N 1))))))))
> 
> Now, here's where it gets weird ......
> If I copy & paste the above expansion and name it as a defun, I can use 
> loops:
> 
> (defun dskdiff (n)
>  (COND ((= N 0) 1)
>        ((= N 1) 2)
>        (T
>         ((LAMBDA (X) (MOD X 12))
>          (+ ((LAMBDA (X) (MOD (* X X) 5)) N)
>             ((LAMBDA (X) (MOD (* -1 X) 7)) (DSKDIFF (- N 1))))))))
> 
> (loop for x to 8 collect (dskdiff x)) ; = (1 2 9 9 6 1 7 4 7)
> 
> ....but if I try to call it a macro, it has no problem returning single 
> values, but blanches at loops:
> 
> (defmacro drewsdiff (n)
>  (make-diff 'n 'drewsdiff
>             '(1 2)
>             '((lambda (x) (mod (* x x) 5))
>               (lambda (x) (mod (* -1 x) 7)))
>             '(lambda (x) (mod x 12))))
> 
> (drewsdiff 8) ; = 7
> (loop for x to 8 collect (drewsdiff x)) ; *** - Lisp stack overflow.
> 
> .... Am I missing something here? Thanks for any help! Gory details 
> below. Drew
> 
> ;; MAKE-DIFF-INITS -- sets all inits for a diff equation
> (defun make-diff-inits (varname initlist)
>  (loop for r to (- (length initlist) 1) collect
>        `((= ,varname ,r) ,(nth r initlist))))
> 
> ;; MAKE-DIFF-FUNC -- sets the 'else' clause for a diff equation
> ;; vname = variable name
> ;; fname = function name
> ;; funclist = lambda functions for n, n-1, n-2 etc.
> ;; resultfunc = global lambda, applied after sum is computed
> (defun make-diff-func (vname fname funclist
>                             &optional (resultfunc '(lambda (x) x)))
>  (let ((funcsum      (append (list '+)
>      (list (list (first funclist) vname))
>             (loop for y from 1 to (- (length funclist) 1) collect
>                   (list (nth y funclist) `(,fname (- ,vname ,y)))))))
>  `(t (,resultfunc ,funcsum))))
> 
> ;; MAKE-DIFF combining inits & functions into a diff equation
> (defun make-diff (vname fname inits funclist
>                                 &optional (resultfunc '(lambda (x) x)))
>  (append (list 'cond) (make-diff-inits vname inits)
>          (list (make-diff-func vname fname funclist resultfunc))))
> 
> 
> 
> 
> 
> 
> 
> 
> 
> 
From: Kaz Kylheku
Subject: Re: macros & loops *blech*
Date: 
Message-ID: <1119393351.304113.47410@g14g2000cwa.googlegroups.com>
Drew Krause wrote:
> (defmacro define-diff (vname fname inits funclist
> 			  &optional (resultfunc '(lambda (x) x)))
>    `(defun ,fname (,vname)
>       ,(make-diff vname fname inits funclist resultfunc)))
>
> (define-diff n
>    dskdiff
>    (1 2)
>    ((lambda (x) (mod (* x x) 5))		; list of rules
>     (lambda (x) (mod (* -1 x) 7)))
>    (lambda (x) (mod x 12)))
>
> CL-USER> (loop for i from 0 to 10 collect (dskdiff i))
> (1 2 9 9 6 1 7 4 7 1 6)

The VNAME looks hokey. The user of DEFINE-DIFF has to specify the
parameter name, which appears to end up consumed for internal use; it
has no reference to anything that the macro user cares about. How about
taking it out of the interface and having the macro invent a parameter
name as a gensym?

(defmacro define-diff (fname inits funclist
                       &optional (resultfunc '(lambda (x) x)))
   (let ((vname (gensym)))
     `(defun ,fname (,vname)
        ,(make-diff vname fname inits funclist resultfunc))))
From: Drew Krause
Subject: Re: macros & loops *blech*
Date: 
Message-ID: <Axeue.10585$eM6.7636@newsread3.news.atl.earthlink.net>
Absolutely. That's exactly what I ended up doing -- thanks

Kaz Kylheku wrote:
> Drew Krause wrote:
> 
>>(defmacro define-diff (vname fname inits funclist
>>			  &optional (resultfunc '(lambda (x) x)))
>>   `(defun ,fname (,vname)
>>      ,(make-diff vname fname inits funclist resultfunc)))
>>
>>(define-diff n
>>   dskdiff
>>   (1 2)
>>   ((lambda (x) (mod (* x x) 5))		; list of rules
>>    (lambda (x) (mod (* -1 x) 7)))
>>   (lambda (x) (mod x 12)))
>>
>>CL-USER> (loop for i from 0 to 10 collect (dskdiff i))
>>(1 2 9 9 6 1 7 4 7 1 6)
> 
> 
> The VNAME looks hokey. The user of DEFINE-DIFF has to specify the
> parameter name, which appears to end up consumed for internal use; it
> has no reference to anything that the macro user cares about. How about
> taking it out of the interface and having the macro invent a parameter
> name as a gensym?
> 
> (defmacro define-diff (fname inits funclist
>                        &optional (resultfunc '(lambda (x) x)))
>    (let ((vname (gensym)))
>      `(defun ,fname (,vname)
>         ,(make-diff vname fname inits funclist resultfunc))))
>