Page 1 of 2

on &rest and keyword args...

Posted: Sat Nov 17, 2012 11:35 am
by megera
HI
I have problems with keyword and rest arguments mixing…
For example, if we have the following func:

Code: Select all

(defun arguments (&rest args &key (a 4) (b 5) (c 6))
	   (list args a b c))

; then I call:
(arguments)
(NIL 4 5 6)   ; and it’s ok , as I expected…
now if I call func with follow args:
(arguments :a 7 :b 8) --> ((:A 7 :B 8) 7 8 6)

but Instead I expected an return like :
(NIL 7 8 6)…

Why CL put this keyword args also in &rest??? I do not understand why this behavior…(and its possible utility... :?: )
Thanks in advance!

Re: on &rest and keyword args...

Posted: Sat Nov 17, 2012 2:11 pm
by abvgdeika

Re: on &rest and keyword args...

Posted: Sat Nov 17, 2012 2:45 pm
by Konfusius
&key may be seen as the same as &rest but with an extra convinience feature of parsing the rest argument as a list of key/value pairs. Common Lisp allows you to use &rest together with &key because sometimes you want to pass on the keyword arguments to another function unchanged. For instance, CLOS makes use of this.

In other words, if you use &rest and &key together then the rest argument contains the list of unparsed key/value arguments.

Re: on &rest and keyword args...

Posted: Sat Nov 17, 2012 3:44 pm
by megera
ok thanks for reporting and fast explanation !!
then when I define a func with &rest and also keyword parameters, I can pass it only keyword args… because it can’t accept (in this case) a variable number of arguments….right?...
but ther’s a way to mix ‘really’ &rest and &key' s behavior ????..
thanks again!!!

Re: on &rest and keyword args...

Posted: Sat Nov 17, 2012 6:08 pm
by Paul
You can always use just &rest, and then parse it yourself for keyword args. It's not entirely trivial...what do you want it to do? Pick off the keyword args and then put everything else in rest? Or let you have non-keyword args first, then the keyword args? Or let you intermix them? How do you tell the difference between a keyword prefacing an &key argument and keyword appearing as an &rest argument?

Re: on &rest and keyword args...

Posted: Sun Nov 18, 2012 6:53 am
by Konfusius
megera wrote:ok thanks for reporting and fast explanation !!
then when I define a func with &rest and also keyword parameters, I can pass it only keyword args… because it can’t accept (in this case) a variable number of arguments….right?...
Yes.
megera wrote:but ther’s a way to mix ‘really’ &rest and &key' s behavior ????..
thanks again!!!
No. As Paul pointed out already, you may parse the keywords in the rest argument manually, but thats inconvinient and inefficient. If you want to pass an extra list of arguments to a function that is taking keyword arguments you may define an extra key that is taking the list of extra arguments.

Code: Select all

(defun arguments (&key (a 4) (b 5) (c 6) args)
      (list args a b c))

Re: on &rest and keyword args...

Posted: Sun Nov 18, 2012 7:04 am
by megera
HI
Thanks also to You Poul for help..
Well, summaries I would just like to know whether it is possible achieve a flexible behavior like in this trivial example of func (written in Python) that can accepts arbitrarily positional variables args(&rest) with keyargs, or only positional variables args, or only keyargs…

Code: Select all

def on_args(*a, k=4, j=5):
    vargs = list(a)
    kargs = [k, j]
    print(vargs, kargs)

#then I can call on_args also in all this ways:

on_args ()
on_args(k=7)
on_args(1,2,3)

for example in CL we could consider a very trivial case like this where I would like to pass only variables args…
but it works only with keyword args instead:

Code: Select all

(defun foo (fn &rest args &key (a 3)(b 4))
	   (progn
	       (if (null args)(setf args 0))
	       (funcall fn args a b)))
FOO
CL-USER> (defun foo1 (&rest args)
	           (let((acc nil))
		           (dolist (i args)
		               (when (not(or (= i 3)(= i 4)))
		                   (push i acc)))
		           acc))
FOO1

CL-USER> (foo  #' foo1)
(0)
; but i can't call (foo #'foo1 1 2 3) for example...
Thanks again for yours
endurance :D

Re: on &rest and keyword args...

Posted: Sun Nov 18, 2012 1:10 pm
by Goheeca
CL has just macros for this kind of issues, so I've created this thing:

Code: Select all

(defun keyname (elem)
	(if (consp elem) (keyname (first elem)) elem))

(defun get-key-part (llist &aux (after (cdr (member '&key llist))))
	(if after
		(subseq after 0
			(position-if #'(lambda (elem)
			                       (member elem '(&allow-other-keys &aux &key &optional &rest)))
			             after))))

(defun get-body-and-docs-decs (body)
	(let ((pos (position-if-not #'(lambda (elem) (or (and (consp elem)
	                                                      (equalp 'declare (first elem)))
	                                                 (and (stringp elem))))
	                            body)))
		(if (and (not pos) (stringp (car (last body))))
			 (setq pos (1- (length body))))
		(values (subseq body 0 pos) (if pos (subseq body pos)))))

(defmacro mydefun (name lambda-list &body body &aux (after-rest (cdr (member '&rest lambda-list))))
	(if (and after-rest
	         (member '&key lambda-list))
		(multiple-value-bind (docs-decs body-forms) (get-body-and-docs-decs body)
			`(defun ,name ,lambda-list ,@docs-decs
			        (loop for key in ',(loop for key in (get-key-part lambda-list) collect (keyname key))
			              for flag = nil then flag
			              do (setq ,(car after-rest)
			                       (delete-if #'(lambda (elem) (cond (flag (setq flag nil) t)
			                                                         (t (if (and (keywordp elem) (string= elem key))
			                                                                (setq flag t)))))
			                                  ,(car after-rest) :count 2)))
			        ,@body-forms))
		`(defun ,name ,lambda-list ,@body)))
After looking into the CLHS I've realized that the aforementioned code is only a half-solution, so you really must parse the &key part yourself and not to pass to defun like I've done.

Re: on &rest and keyword args...

Posted: Sun Nov 18, 2012 4:44 pm
by Paul
megera wrote:Well, summaries I would just like to know whether it is possible achieve a flexible behavior like in this trivial example of func (written in Python) that can accepts arbitrarily positional variables args(&rest) with keyargs, or only positional variables args, or only keyargs…
Only if you write the argument-parsing function yourself. Handling named (keyword) arguments is slow, so Lisp doesn't do it if it doesn't have to. Python doesn't care much about efficiency. [BTW: that putting the &rest arg (*a) first is an error in Python!]

Re: on &rest and keyword args...

Posted: Mon Nov 19, 2012 3:16 am
by megera
Hi Goheeca and thanks for your code ;) ..I'm studying it becouse is still too advanced for my current low degree :o :o for examp. I still have not studied macros..but I’m close ;))
Thanks Paul!!now the questions about args begins to become clear !! :D Thanks very much to all !!! :mrgreen:

ps.Paul why do You say that putting the &rest arg (*a) first is an error in Python?(they aren't "default" args) ...
in the header , after *vargs we can't put default args but we can put only "keyword" arguments and **variables keyword args:

def foo(b, x=1, y=2, *vargs, k=4, j=5, **kargs): pass #here x and y are default positionals args, k and j are Keyword-only args instead