on &rest and keyword args...

Discussion of Common Lisp

on &rest and keyword args...

Postby megera » Sat Nov 17, 2012 11:35 am

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!
megera
 
Posts: 30
Joined: Wed Oct 10, 2012 1:34 pm

Re: on &rest and keyword args...

Postby abvgdeika » Sat Nov 17, 2012 2:11 pm

To understand LISP, you must first understand LISP.
abvgdeika
 
Posts: 20
Joined: Mon Jun 06, 2011 10:59 pm

Re: on &rest and keyword args...

Postby Konfusius » Sat Nov 17, 2012 2:45 pm

&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.
Konfusius
 
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: on &rest and keyword args...

Postby megera » Sat Nov 17, 2012 3:44 pm

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!!!
megera
 
Posts: 30
Joined: Wed Oct 10, 2012 1:34 pm

Re: on &rest and keyword args...

Postby Paul » Sat Nov 17, 2012 6:08 pm

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?
Paul
 
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: on &rest and keyword args...

Postby Konfusius » Sun Nov 18, 2012 6:53 am

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))
Konfusius
 
Posts: 62
Joined: Fri Jun 10, 2011 6:38 am

Re: on &rest and keyword args...

Postby megera » Sun Nov 18, 2012 7:04 am

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
megera
 
Posts: 30
Joined: Wed Oct 10, 2012 1:34 pm

Re: on &rest and keyword args...

Postby Goheeca » Sun Nov 18, 2012 1:10 pm

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.
cl-2dsyntax is my attempt to create a Python-like reader. My mirror of CLHS (and the dark themed version).
User avatar
Goheeca
 
Posts: 218
Joined: Thu May 10, 2012 12:54 pm

Re: on &rest and keyword args...

Postby Paul » Sun Nov 18, 2012 4:44 pm

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!]
Paul
 
Posts: 106
Joined: Tue Jun 02, 2009 6:00 am

Re: on &rest and keyword args...

Postby megera » Mon Nov 19, 2012 3:16 am

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
megera
 
Posts: 30
Joined: Wed Oct 10, 2012 1:34 pm

Next

Return to Common Lisp

Who is online

Users browsing this forum: No registered users and 2 guests

cron