From: Andy Reiter
Subject: How can I get the "argument list".
Date: 
Message-ID: <d4b78695.0207232033.57b35a9@posting.google.com>
Ok, here is a little wicked idea. Wouldn't it be good if you had access to the
list of arguments, passed to a function, in one list variable?

For example, you have:

(defun f(arg0 arg1 arg1 .. argn)
  ; and in the body of the function, instead of using each argument by its symbol
  ; you get the magical variable *args* which just happens to hold the argument
  ; list, and awaits you patiently. 
  )

I got the idea while re-re-re-reading SICP; there was a question [1.3] requiring
you to find the two greatest number from the three arguments, and do something
with them (square them and add, for the curious.)

But I learnt Lisp from Paul Graham first, and it REALLY seemed liked a problem
begging for generalization. I wanted to lift the 3-arguments requirement, and
make one function which mapcarable over a list of numbers. This in turn would
use "sort" with the appropriate :test, and another function which would return
n greatest numbers (which could be a closure which returns first/second/elt)

My question is, am I abusing the generalization thinggie, and if not, is there
someway to get a list of the arguments passed to a function?

From: Joel Ray Holveck
Subject: Re: How can I get the "argument list".
Date: 
Message-ID: <y7cptxdaes4.fsf@sindri.juniper.net>
> Ok, here is a little wicked idea. Wouldn't it be good if you had access to the
> list of arguments, passed to a function, in one list variable?
> For example, you have:
> (defun f(arg0 arg1 arg1 .. argn)
>   ; and in the body of the function, instead of using each argument by its symbol
>   ; you get the magical variable *args* which just happens to hold the argument
>   ; list, and awaits you patiently. 
>   )

You mean like:

  (defun f (&rest args)
    (loop for elt in args
          sum (expt elt 2)))

Is this what you're talking about?  Since you're reading SICP, I'll
add a Scheme version, with the caveat that it's not written to be
efficient:

  (define (f . args)
    (if (null? args)
        0
        (+ (* (car args) (car args))
           (apply f (cdr args)))))

If you want to generalize the function-- say, allow a function to be
passed instead of always squaring (if you're reading SICP I know
that's on your mind):

  (defun f (fn &rest args)
    (loop for elt in args
          sum (funcall fn elt)))
  ;; call like (f #'(lambda (x) (expt x 2)) 1 2 3)

In Scheme:

  (define (f fn . args))
    (if (null? args)
        0
        (+ (fn (car args))
           (apply f fn (cdr args)))))
  ;; call like (f (lambda (x) (* x x)) 1 2 3)

or if you want allow the user to specify a second function instead of
always summing:

  (defun f (preprocess-fn reduce-fn &rest args)
    (reduce reduce-fn (mapcar preprocess-fn args)))
  ;; call like (f #'(lambda (x) (expt x 2)) #'+ 1 2 3)

(The Scheme version is left as an exercise to the reader.)

Your email asked about having the *entire* argument list in a
variable.  Admittedly, the second and third examples don't have the
entire argument list in a variable, but I think they serve your
purpose.

If, for some deranged reason, you still want the entire arg list
available in the function, you can do so thusly:

  (defun f (&rest all-args)
    (destructuring-bind (preprocess-fn reduce-fn &rest args) all-args
      (declare (ignore args))
      (reduce reduce-fn (mapcar preprocess-fn (cddr all-args)))))
  ;; call like (f #'(lambda (x) (expt x 2)) #'+ 1 2 3)

Does this help?

joelh
From: Andrew Philpot
Subject: Re: How can I get the "argument list".
Date: 
Message-ID: <ahlctl$jtr$1@altamira.isi.edu>
In article <···························@posting.google.com>,
Andy Reiter <·······@flop.co.uk> wrote:

>Ok, here is a little wicked idea. Wouldn't it be good if you had access
>to the list of arguments, passed to a function, in one list variable?
>
>For example, you have:
>
>(defun f(arg0 arg1 arg1 .. argn)
>  ; and in the body of the function, instead of using each argument by
>  ; its symbol 
>  ; you get the magical variable *args* which just happens to hold the
>  ; argument 
>  ; list, and awaits you patiently. 
>  )
>
> I got the idea while re-re-re-reading SICP; there was a question [1.3]
>requiring you to find the two greatest number from the three arguments,
>and do something with them (square them and add, for the curious.)
>
>But I learnt Lisp from Paul Graham first, and it REALLY seemed liked a
>problem begging for generalization. I wanted to lift the 3-arguments
>requirement, and make one function which mapcarable over a list of
>numbers. This in turn would use "sort" with the appropriate :test, and
>another function which would return n greatest numbers (which could be
>a closure which returns first/second/elt)
>
> My question is, am I abusing the generalization thinggie, and if not,
>is there someway to get a list of the arguments passed to a function?

I'm guessing here you want something more than just:

(defun f (&rest *args*)
  )

But suppose you say you would like _both_ *args* _and_ a declared
argument list.

(defmacro defun-with-args (name varlist &rest body)
  `(defun ,name (&rest *args*)
     (declare (ignorable *args*))
     (destructuring-bind ,varlist *args*
       ,@body)))

(defun-with-args foo (bar &key baz)
  (list bar baz *args*))

(foo 1 :baz 2) ==>
(1 2 (1 :BAZ 2))
-- 
Dunderhead
From: Joel Ray Holveck
Subject: Re: How can I get the "argument list".
Date: 
Message-ID: <y7c7kjl1qup.fsf@sindri.juniper.net>
> I'm guessing here you want something more than just:
> (defun f (&rest *args*)
>   )
> But suppose you say you would like _both_ *args* _and_ a declared
> argument list.
> (defmacro defun-with-args (name varlist &rest body)
>   `(defun ,name (&rest *args*)
>      (declare (ignorable *args*))
>      (destructuring-bind ,varlist *args*
>        ,@body)))
> (defun-with-args foo (bar &key baz)
>   (list bar baz *args*))

Here's an alternative with a bit of flexibility:

(defmacro defun* (name arglist &body body)
  (let ((entire-arglist (gensym)))
    `(defun ,name (&rest ,entire-arglist)
       (destructuring-bind ,arglist ,entire-arglist
         ,@body))))

This can be used identically to DEFUN, but it gives you a
destructuring lambda list, including &WHOLE.  Example:

  (defun* foo (&whole them a b c)
    ;; Within here, A, B, and C are the three arguments to FOO, and
    ;; THEM is a list of the args.
    )

(Naturally, THEM won't have the name of the called function in it,
like it would in a macro.)

Cheers,
joelh
From: Roger Corman
Subject: Re: How can I get the "argument list".
Date: 
Message-ID: <3d3e47a2.165522819@news.sf.sbcglobal.net>
On 23 Jul 2002 21:33:24 -0700, ·······@flop.co.uk (Andy Reiter) wrote:

>Ok, here is a little wicked idea. Wouldn't it be good if you had access to the
>list of arguments, passed to a function, in one list variable?
>
>For example, you have:
>
>(defun f(arg0 arg1 arg1 .. argn)
>  ; and in the body of the function, instead of using each argument by its symbol
>  ; you get the magical variable *args* which just happens to hold the argument
>  ; list, and awaits you patiently. 
>  )

Here's a slight modification to DEFUN, called DEFUNX, which does this:

(defparameter *args* nil)
(defmacro defunx (name (&rest arglist) &body body)
    `(defun ,name (&rest *args*)
        (apply (lambda ,arglist ,@body) *args*)

You can use it like this:

(defunx foo (x y z)
    (format t "x=~A, y=~A, z=~A, *args*=~A~%" x y z *args*)
    (apply #'+ *args*))

(foo 1 2 3) 
x=1, y=2, z=3, *args*=(1 2 3)
6

This should work fine for functions with &optional and &rest args. If you wanted
to add support for keyword arguments you would need to add a little more logic
to insert the *args* variable in the proper place in the lambda list.

Roger
From: Duane Rettig
Subject: Re: How can I get the "argument list".
Date: 
Message-ID: <465yui2xz.fsf@beta.franz.com>
[Sorry for the late response; I've been on vacation]

·······@flop.co.uk (Andy Reiter) writes:

> Ok, here is a little wicked idea. Wouldn't it be good if you had access to the
> list of arguments, passed to a function, in one list variable?

I think probably the most "wicked" part of your request is the requirement that
the arguments be represented as a list.  Lists are generated by creating cons
cells (whether on the stack or in the lisp heap) and are thus less efficient than
pure argument passing, depending on the implementation.

> For example, you have:
> 
> (defun f(arg0 arg1 arg1 .. argn)
>   ; and in the body of the function, instead of using each argument by its symbol
>   ; you get the magical variable *args* which just happens to hold the argument
>   ; list, and awaits you patiently. 
>   )
> 
> I got the idea while re-re-re-reading SICP; there was a question [1.3] requiring
> you to find the two greatest number from the three arguments, and do something
> with them (square them and add, for the curious.)
> 
> But I learnt Lisp from Paul Graham first, and it REALLY seemed liked a problem
> begging for generalization. I wanted to lift the 3-arguments requirement, and
> make one function which mapcarable over a list of numbers. This in turn would
> use "sort" with the appropriate :test, and another function which would return
> n greatest numbers (which could be a closure which returns first/second/elt)

If you are working on a specific problem which you want to generalize, it is
sometimes more efficient to define the functionality recursively or to break it
down into smaller problems than to try to generalize the function itself.  And
if the more general problem works best on all arguments at once, then perhaps the
extra consing of the &rest argument is worth it, as others have pointed out
(perhaps with a dynamic-extent declaration, to keep the conses on the stack).

> My question is, am I abusing the generalization thinggie, and if not, is there
> someway to get a list of the arguments passed to a function?

There may be another way to generalize things; most of the time we want argument
passing to be as efficient as possible, but the reason for wanting the arguments
in list form is for debugging purposes (note that some of the others who answered
your question did so with debug-style solutions).  Suppose you had a function:

(defun foo (arg0 arg1 arg2 arg3)
 ... )

and wanted to be able to print out the arguments, however many were passed, so
as to debug your applcation?  I think (prejudicially, of course) that the
fwrap facility of Allegro CL separates the runtime from the debug nicely and
accomplishes the dual requirements of allowing efficient argment access as
well as full debug access.  Additionally, when you "fwrap" a function there
is no need to recompile or even redefine the original function (this is most
useful in situations where you do not have the source to a function but must
debug it or its callers).

Example:

CL-USER(1): (defun bar (w x y z) nil) ;; just a dummy for demo

BAR
CL-USER(2): (compile (defun foo (arg0 arg1 arg2 arg3)
                       (bar arg0 arg1 arg2 arg3)))
FOO
NIL
NIL
CL-USER(3): (compile (def-fwrapper foo-rest-wrapper (&rest args)
                       (format t "foo's args are: ~s~%" args)
                       (call-next-fwrapper)))
FOO-REST-WRAPPER
NIL
NIL
CL-USER(4): (fwrap 'foo :rest-wrapper 'foo-rest-wrapper)
#<Function FOO>
CL-USER(5): (foo 1)  ;; Interntionally given too few arguments
foo's args are: (1)
Error: FOO got 1 arg, wanted 4 args.
  [condition type: PROGRAM-ERROR]

Restart actions (select using :continue):
 0: Return to Top Level (an "abort" restart).
 1: Abort entirely from this process.
[1] CL-USER(6): :res
CL-USER(7): (foo 10 20 30 40)
foo's args are: (10 20 30 40)
NIL
CL-USER(8): 

Of course, you can just as easily define an fwrap function which has the
same destructuring as the primary function (though that is beyond the scope
of your question):

CL-USER(8): (compile (def-fwrapper foo-destructured-wrapper (arg0 arg1 arg2 arg3)
                       (format t "foo's individual arguments are ~s ~s ~s ~s~%"
                                 arg0 arg1 arg2 arg3)
                       (call-next-fwrapper)))
FOO-DESTRUCTURED-WRAPPER
NIL
NIL
CL-USER(9): (fwrap 'foo :normal-wrapper 'foo-destructured-wrapper)
#<Function FOO>
CL-USER(10): (describe #'foo)
#<Function FOO> is a COMPILED-FUNCTION.
  The arguments are (ARG0 ARG1 ARG2 ARG3)
  It has the following indicator/fwrapper pairs, from outer to inner:
:NORMAL-WRAPPER   #<Function FOO-DESTRUCTURED-WRAPPER>
:REST-WRAPPER     #<Function FOO-REST-WRAPPER>
CL-USER(11): (foo 10 20 30 40)
foo's individual arguments are 10 20 30 40
foo's args are: (10 20 30 40)
NIL
CL-USER(12): 

-- 
Duane Rettig          Franz Inc.            http://www.franz.com/ (www)
1995 University Ave Suite 275  Berkeley, CA 94704
Phone: (510) 548-3600; FAX: (510) 548-8253   ·····@Franz.COM (internet)