From: normanj
Subject: Need help for a macro with variable arguments
Date: 
Message-ID: <0341c3f2-48ee-4917-bc22-8e9eafe4dd15@i29g2000prf.googlegroups.com>
Hi All,

I want a macro like this:
(test-args 2 3  ;; the number of integers here may vary.
  (print 'action1)
  (print 'action2))

to be expanded to:
(progn
  (setf item-count 2)
  (setf item1 (get-item 2))
  (setf item2 (get-item 3))
  (print 'action1)
  (print 'action2))

The purpose of this macro is to get specified items from a list, and
save them in variables, then the following actions can use those
variables directly.

I tried several methods, and finally I found that I didn't really
understand how macro works. At first, I thought macros cannot execute
any code, they just replace strings with strings. But, the following
macro can work, so I'm wrong.
 (defmacro pattern (&body body)
  (cons 'progn
    (loop for (condition action) in body collect
	  `(if ,condition
	    ,action
	    nil))))

Then, I thought that macros are just like functions, they can execute
any code like functions can do. The only difference is macros don't
evaluate the input and output. But, this can't explain why the
following macro can't work:

(defmacro test-args (&body body)
  (let ((in-body nil) (len 0) (index 0))
    (setf len (length body))
    (when (and (not in-body)
	       (< index len))
      (print index)
      (if (typep (nth index body) 'integer)
	  (progn
	    (print (nth index body))
	    (setf index (1+ index)))
	  (setf in-body T))
      (format t "index: ~D len: ~D in-body: ~A~%"
	      index len in-body))
    (format t "index: ~D len: ~D~%" index len)
    `(progn
       (print (list ,index))
       ,@body)))

(test-args 1 2 3 4
   (print 'OK))

Thanks in advance.

Best regards,
Norman

From: danb
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <ee8e46d2-b175-4901-bc8f-d5e7e438258e@o77g2000hsf.googlegroups.com>
On Mar 10, 2:46 am, normanj <·······@gmail.com> wrote:
> The purpose of this macro is to get specified items from a list, and
> save them in variables, then the following actions can use those
> variables directly.

What do you want to call the variables?  Normally, you would pass
their names to the macro.  If all you need is fast access, another
option would be to just coerce the list to an array.  If you insist on
generating variables called "itemN", you'll have to mess with stuff
like
  (intern (concatenate 'string "item" (format nil "~D" n)))
(this is untested)

> macros are just like functions, they can execute
> any code like functions can do.

Right.  Macros are functions that are executed just before
compilation.

> macros don't evaluate the input and output.

Macro output is evaluated.  Function output is not.

A macro recieves unevaluated code from the reader,
processes it in the same way that a function processes
lists, and returns the expanded code to eval, which
evaluates it.

--Dan

-------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Ken Tilton
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <47d4fd61$0$15195$607ed4bc@cv.net>
normanj wrote:
> Hi All,
> 
> I want a macro like this:
> (test-args 2 3  ;; the number of integers here may vary.
>   (print 'action1)
>   (print 'action2))
> 
> to be expanded to:
> (progn
>   (setf item-count 2)
>   (setf item1 (get-item 2))
>   (setf item2 (get-item 3))
>   (print 'action1)
>   (print 'action2))

(a) item1 and item2 look like they are meant to be special variables, so 
make your life easier and use *item-1* and *item-2*. I threw in the 
hyphens for free. :)

(b) What you want is puzzling. What if three integers were offered? Do 
you then want a special called item3 to be bound? Why not have an *item* 
vector? I understand you are distilling things to learn how macros work 
and make your question clearer, but in the last regard it might be 
having the opposite effect because my brain tends to explode if I try to 
help someone do something and I cannot tell what they are trying to do.


> 
> The purpose of this macro is to get specified items from a list, and
> save them in variables, then the following actions can use those
> variables directly.
> 
> I tried several methods, and finally I found that I didn't really
> understand how macro works. At first, I thought macros cannot execute
> any code, they just replace strings with strings. But, the following
> macro can work, so I'm wrong.

Correct. A macro is normal code that runs at an unusual time: before the 
compiler gets its hand on the macro body. The macro function gets to 
mangle that as it sees fit (again, using normal Lisp code) before 
handing it off to the compiler.

>  (defmacro pattern (&body body)
>   (cons 'progn
>     (loop for (condition action) in body collect
> 	  `(if ,condition
> 	    ,action
> 	    nil))))
> 
> Then, I thought that macros are just like functions, they can execute
> any code like functions can do. The only difference is macros don't
> evaluate the input and output. But, this can't explain why the
> following macro can't work:
> 
> (defmacro test-args (&body body)
>   (let ((in-body nil) (len 0) (index 0))
>     (setf len (length body))
>     (when (and (not in-body)
> 	       (< index len))
>       (print index)
>       (if (typep (nth index body) 'integer)
> 	  (progn
> 	    (print (nth index body))
> 	    (setf index (1+ index)))
> 	  (setf in-body T))
>       (format t "index: ~D len: ~D in-body: ~A~%"
> 	      index len in-body))
>     (format t "index: ~D len: ~D~%" index len)
>     `(progn
>        (print (list ,index))
>        ,@body)))
> 
> (test-args 1 2 3 4
>    (print 'OK))

That code looks like it want to be in a loop (with the variable index 
incremented at each iteration) but is not.

btw, quick tip: bundle up the macro parameters in its own sublist:

(test-args (2 3)
    (print 'action)
    (print 'action))

Then you do not have to write the code that guess when you have left the 
items and entered the body.

kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: normanj
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <af65d816-face-4fae-915d-14e5b34983d6@s12g2000prg.googlegroups.com>
It seems like the example is not clear enough. Sorry. Let me try to
describe it more clearly:

(test-args 2 3
  (action1)
  (action2))

There are three macros here: test-args, action1, action2.

(defmaco action1 ()
  `(proc1 curr))
(defmaco action2 ()
  `(proc2 curr))

The expanded code will be:
(let (curr)
  (push 2 curr)                     ;; number of integer
  (push (get-item 2) curr)          ;; first parameter
  (push (get-item 3) curr)          ;; second parameter
  (setf curr (reverse curr))
  (proc1 curr)
  (proc2 curr))
From: danb
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <7da45161-8ad8-4a10-a5b7-03b4d0998d29@h25g2000hsf.googlegroups.com>
On Mar 10, 9:59 am, normanj <·······@gmail.com> wrote:
> It seems like the example is not clear enough.
> Sorry. Let me try to describe it more clearly:

Your new example isn't clearer, it's just different.

> There are three macros here: test-args, action1, action2.
> (defmaco action1 () `(proc1 curr))
> (defmaco action2 () `(proc2 curr))

You don't need those macros.  Just pass proc1 and proc2
to test-args directly.

> The expanded code will be:
> (let (curr)
>   (push 2 curr)                     ;; number of integer
>   (push (get-item 2) curr)          ;; first parameter
>   (push (get-item 3) curr)          ;; second parameter
>   (setf curr (reverse curr))
>   (proc1 curr)
>   (proc2 curr))

This isn't tested, but assuming (push 2 curr) refers to
the number of arguments, it might be close:

(defmacro test-args (args procs)
  (let ((curr (gensym))
        (pushes (mapcar (lambda (arg) `(push (get-item ,arg) ,curr))
                        args))
        (calls  (mapcar (lambda (proc) `(,proc ,curr))
                        procs)))
   `(let ((curr (list ,(length args))))
      ,@pushes
      (setf ,curr (reverse ,curr))
      ,@calls)))

--Dan

------------------------------------------------
Dan Bensen
http://www.prairienet.org/~dsb/
From: Thomas A. Russ
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <ymiabl6h1jv.fsf@blackcat.isi.edu>
normanj <·······@gmail.com> writes:

> It seems like the example is not clear enough. Sorry. Let me try to
> describe it more clearly:
> 
> (test-args 2 3
>   (action1)
>   (action2))
> 
> There are three macros here: test-args, action1, action2.
> 
> (defmaco action1 ()
>   `(proc1 curr))
> (defmaco action2 ()
>   `(proc2 curr))

But I think these are superfluous.  Not to mention that it is generally
had to maintain macros that expand into code that depends on the
environment being setup exactly correctly (in this case having CURR
bound) in order for them to work.  I would just replace them with
appropriate expansions in-line.  If the real code is hairier than you
show here, you should replace the macros with ones that take the
variable of interest instead -- assuming you can't just use a function.

> 
> The expanded code will be:
> (let (curr)
>   (push 2 curr)                     ;; number of integer
>   (push (get-item 2) curr)          ;; first parameter
>   (push (get-item 3) curr)          ;; second parameter
>   (setf curr (reverse curr))
>   (proc1 curr)
>   (proc2 curr))

OK.  My approach would be

(defmacro test-args ((var &rest indices) &body body)
  ;; Clever:  Build this in reverse order at macro-expansion
  ;;          time to avoid having to reverse at run-time!
  (let ((preamble nil))
    (dolist (index (reverse indices))
      (push `(push (get-item ,index) ,var)))
    (push `(push ,(length indices) ,var))
    `(let (,var)
       ,@preamble
       ,@body)))

And then I would invoke it with:

(test-args (curr 1 2 3)
  (proc1 curr)
  (proc2 curr))

That way, all of the information is explicit in the call, including the
specification of the variable that gets bound.  That eliminates the need
to have an implicit variable name used to establish the communication
between the hidden parts.  (And also eliminating the problem if someone
else wants to pass a body that binds the symbol CURR to something else
in the BODY part).


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal J. Bourguignon
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <7ctzjese18.fsf@pbourguignon.anevia.com>
normanj <·······@gmail.com> writes:

> It seems like the example is not clear enough. Sorry. Let me try to
> describe it more clearly:
>
> (test-args 2 3
>   (action1)
>   (action2))
>
> There are three macros here: test-args, action1, action2.
>
> (defmaco action1 ()
>   `(proc1 curr))
> (defmaco action2 ()
>   `(proc2 curr))
>
> The expanded code will be:
> (let (curr)
>   (push 2 curr)                     ;; number of integer
>   (push (get-item 2) curr)          ;; first parameter
>   (push (get-item 3) curr)          ;; second parameter
>   (setf curr (reverse curr))
>   (proc1 curr)
>   (proc2 curr))


So you know what you want, good.  I guess you know how to write a _function_ to do that.

(defun generate-test-args (arg1 arg2 body)
   ...)

such as: (generate-test-args 2 3 '((action1) (action2)))
would return:

(let (curr)
  (push 2 curr)                     ;; number of integer
  (push (get-item 2) curr)          ;; first parameter
  (push (get-item 3) curr)          ;; second parameter
  (setf curr (reverse curr))
  (action1)
  (action2))


Well, if you can write that function, you're done.  Just add:

(defmacro test-args (arg1 arg2 &body body)
  (generate-test-args arg1 arg2 body))



See also:
http://groups.google.com/group/comp.lang.lisp/msg/03e113c865fa41b4
http://groups.google.com/group/comp.lang.lisp/msg/3df6d5e4f81ce6f2




Just deal with one macro at a time: the compiler is smart enough to
expand embedded macros in the code you generate from another one.


It looks like you want a variable number of arguments.  Macros lambda
expressions allow you to do that easily:

(test-args (2 3 4 5) 
    (action1)
    (action2)
    (action3))

--> (defmacro test-args ((&rest args) &body body) 
       (generate-test-args args body))

(and change generate-test-args accordingly).


-- 
__Pascal Bourguignon__
From: normanj
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <4133bb3d-3170-4725-a012-defa22913682@e10g2000prf.googlegroups.com>
> btw, quick tip: bundle up the macro parameters in its own sublist:
>
> (test-args (2 3)
>     (print 'action)
>     (print 'action))
>
> Then you do not have to write the code that guess when you have left the
> items and entered the body.

Yes, this is my current choice. But, I'm just wondering if there is
another way to remove the parentheses. Not everyone like them.

Norman
From: Thomas A. Russ
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <ymi63vuh1gh.fsf@blackcat.isi.edu>
normanj <·······@gmail.com> writes:

> > btw, quick tip: bundle up the macro parameters in its own sublist:
> >
> > (test-args (2 3)
> >     (print 'action)
> >     (print 'action))
> >
> > Then you do not have to write the code that guess when you have left the
> > items and entered the body.
>
> Yes, this is my current choice. But, I'm just wondering if there is
> another way to remove the parentheses. Not everyone like them.

Well, you could remove them from there, but I think it makes it much
less clear what is being done.  It also makes it impossible to, for
example, pass in the indices as variables rather than constants --
although I guess you could test for atoms.

You could do it, but it would be closer to lisp style to use the extra
level of nesting, since that establishes the syntax for your construct
and actually helps by doing the grouping explicitly.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Ken Tilton
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <47d56806$0$25037$607ed4bc@cv.net>
normanj wrote:
>>btw, quick tip: bundle up the macro parameters in its own sublist:
>>
>>(test-args (2 3)
>>    (print 'action)
>>    (print 'action))
>>
>>Then you do not have to write the code that guess when you have left the
>>items and entered the body.
> 
> 
> Yes, this is my current choice. But, I'm just wondering if there is
> another way to remove the parentheses. Not everyone like them.

Take those people out and shoot them, keep the parens.

hth, kenny

-- 
http://smuglispweeny.blogspot.com/
http://www.theoryyalgebra.com/

"In the morning, hear the Way;
  in the evening, die content!"
                     -- Confucius
From: John Thingstad
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <op.t7slozidut4oq5@pandora.alfanett.no>
P� Mon, 10 Mar 2008 08:46:14 +0100, skrev normanj <·······@gmail.com>:

> Hi All,
>
> I want a macro like this:
> (test-args 2 3  ;; the number of integers here may vary.
>   (print 'action1)
>   (print 'action2))
>
> to be expanded to:
> (progn
>   (setf item-count 2)
>   (setf item1 (get-item 2))
>   (setf item2 (get-item 3))
>   (print 'action1)
>   (print 'action2))
>
> The purpose of this macro is to get specified items from a list, and
> save them in variables, then the following actions can use those
> variables directly.
>
> I tried several methods, and finally I found that I didn't really
> understand how macro works. At first, I thought macros cannot execute
> any code, they just replace strings with strings. But, the following
> macro can work, so I'm wrong.
>  (defmacro pattern (&body body)
>   (cons 'progn
>     (loop for (condition action) in body collect
> 	  `(if ,condition
> 	    ,action
> 	    nil))))
>
> Then, I thought that macros are just like functions, they can execute
> any code like functions can do. The only difference is macros don't
> evaluate the input and output. But, this can't explain why the
> following macro can't work:
>
> (defmacro test-args (&body body)
>   (let ((in-body nil) (len 0) (index 0))
>     (setf len (length body))
>     (when (and (not in-body)
> 	       (< index len))
>       (print index)
>       (if (typep (nth index body) 'integer)
> 	  (progn
> 	    (print (nth index body))
> 	    (setf index (1+ index)))
> 	  (setf in-body T))
>       (format t "index: ~D len: ~D in-body: ~A~%"
> 	      index len in-body))
>     (format t "index: ~D len: ~D~%" index len)
>     `(progn
>        (print (list ,index))
>        ,@body)))
>
> (test-args 1 2 3 4
>    (print 'OK))
>
> Thanks in advance.
>
> Best regards,
> Norman

Rather than go into detail on this example perhaps it would be better to  
read "On Lisp" by Paul Graham
http://www.paulgraham.com/onlisp.html available for free download.

I might add &rest is used for variable arguments.

--------------
John Thingstad
From: Sohail Somani
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <CNfBj.75259$FO1.54998@edtnps82>
On Mon, 10 Mar 2008 00:46:14 -0700, normanj wrote:

> I want a macro like this:
> (test-args 2 3  ;; the number of integers here may vary.
>   (print 'action1)
>   (print 'action2))

Whats wrong with:

(defmacro test-args ((&rest the-ints) &body body) ... )

Then you call it like:

(test-args (2 3)
  ... )

-- 
Sohail Somani
http://uint32t.blogspot.com
From: normanj
Subject: Re: Need help for a macro with variable arguments
Date: 
Message-ID: <3f2794af-53df-46f9-b7a8-75a07fca7e4b@s13g2000prd.googlegroups.com>
>
> Whats wrong with:
>
> (defmacro test-args ((&rest the-ints) &body body) ... )
>
> Then you call it like:
>
> (test-args (2 3)
>   ... )

This solution is much more reasonable. Thanks! Thanks All!
I got the answer and explanation about Lisp macros I need.

Best regards,
Norman