问题
I want to use both &rest
and &key
at the same time. However, the attempted code below:
(defun test (&rest args &key (name "who")) nil)
(test 1 2 3 4 5 :name "hoge")
causes an error:
*** - TEST: keyword arguments in (1 2 3 4 5 :NAME "hoge") should occur pairwise
and when I gives only keyword parameter like (test :name "hoge")
, it works. Is it possible to use both &rest and &key?
回答1:
It's generally not a good idea to mix rest parameters with keyword parameters within a function definition in Common Lisp. If you do so, you should probably consider rewriting the function definition because it can lead to some unexpected behavior. If both &rest and &key appear in a parameter list, then both things happen--all the remaining values, which include the keywords themselves, are gathered into a list that's bound to the &rest parameter, and the appropriate values are also bound to the &key parameters. So the (name "who") keyword parameter is bound to your list of rest parameters by default. if you try to enter the arguments (1 2 3 4 5), you will get an error because they aren't bound to your parameter (name "who"). Here is an example:
(defun test (&rest args &key (name "who"))
(list args name))
Here we have your function definition. If we try to call the function which return a list of the arguments, we will see that the &rest parameters are bound to they &key parameters here:
CL-USER> (test :name "Davis")
((:NAME "Davis") "Davis")
By mixing &rest parameters and keyword parameters in the same parameter list, you won't be able to enter any rest parameters that don't match your keyword parameter which is why you enter into a breakloop here.
Now, if you want to create a macro, you can technically use multiple parameter lists within the definition, and add keyword parameters in one list, and &rest (or &body) parameters in the other list:
(defmacro hack-test ((&key (name "who")) &body body)
`(list ,name ,@body))
CL-USER> (hack-test (:name "Ricky")
(+ 2 3))
("Ricky" 5)
CL-USER> (hack-test ()
(+ 2 4)
(+ 4 5)
(+ 9 9))
("who" 6 9 18)
CL-USER>
回答2:
The combination of &key and &rest are actually very common in Common Lisp, but almost always together with &allow-other-keys.
E.g., suppose you want to define a wrapper for write but do not want to list explicitly all the keyword arguments it takes:
(defun my-write (object &rest args &key stream &allow-other-keys)
(write "my wrapper" :stream stream)
(apply #'write object args))
You will find many places where this &rest/&key/&allow-other-keys patterns is used wherever the CLOS is actually implemented.
回答3:
Here's an example of how you might do what you want to do. This is fairly simple-minded, but it allows you to define functions which take any number of arguments, together with zero or more keyword arguments. There is then a little trampoline which pulls keywords and their values out of the arguments and calls the function appropriately.
This is not meant to be production-quality code: it would clearly be better to have the trampoline-making function know exactly what keywords it was looking for for instance, which could be known, rather than just 'any keywords'.
(defun make-kw-trampoline (fn)
;; Given a function which takes a single rest arg and a bunch of
;; keyword args, return a function which will extract the keywords
;; from a big rest list and call it appropriately
(lambda (&rest args)
(loop for (arg . rest) on args
if (keywordp arg)
if (not (null rest))
collect arg into kws and collect (first rest) into kws
else do (error "Unpaired keyword ~S" arg)
finally (return (apply fn args kws)))))
(defmacro defun/rest/kw (name (rest-arg and-key . kw-specs) &body decls-and-forms)
;; Define a function which can take any number of arguments and zero
;; or more keyword arguments.
(unless (eql and-key '&key)
(error "um"))
(multiple-value-bind (decls forms) (loop for (thing . rest) on decls-and-forms
while (and (consp thing)
(eql (first thing) 'declare))
collect thing into decls
finally (return
(values decls (cons thing rest))))
`(progn
(setf (fdefinition ',name)
(make-kw-trampoline (lambda (,rest-arg &key ,@kw-specs)
,@decls
(block ,name
,@forms))))
',name)))
So if I now define a function like this:
(defun/rest/kw foo (args &key (x 1 xp))
(declare (optimize debug))
(values args x xp))
Then I can call it so:
> (foo 1 2 3)
(1 2 3)
1
t
> (foo 1 2 :x 4 3)
(1 2 :x 4 3)
4
t
Note that defun/rest/kw
may not do the same thing that defun
does: in particular I think it does enough to define the function properly (and not to define it at compile time) but the compiler may not realise the function exists at compile time (so there may be warnings), and it also does not do any implementation-specific magic.
回答4:
What you're trying to do is not supported. In Common Lisp, the trailing "rest" arguments of a variadic function coincide with the keyword arguments. That is to say, when a function has keyword parameters, what it means that the trailing arguments of a function (those which follow the fixed and optional arguments) are parsed as keyword parameters
When you specify that there are both &key
and &rest
parameters, it means that the trailing arguments are captured as a list, and that these arguments are also parsed into keyword arguments.
What I'm getting at is that it isn't the case that the keyword arguments begin at some unspecified position within the trailing parameters.
If we want the call
(test 1 2 3 4 5 :NAME "hoge")
to work, then he function has to have five fixed-position parameters (some combination of required and optional parameters adding up to five). Then the trailing parameters are :name
and "hoge"
. If a &rest
parameters is present, then it captures the list (:name "hoge")
.
If it is necessary that the rest list is (1 2 3 4 5 ...)
and that the keyword parameters begin at the first keyword, you have code that yourself.
The most obvious approach:
(defun test (&rest args)
(let* ((keys (member-if #'keywordp args))
(nonkeys (ldiff args keys)))
(destructuring-bind (&key name) keys
(list nonkeys name))))
Test:
[1]> (test 1 2 3 4 5 :name "hoge")
((1 2 3 4 5) "hoge")
来源:https://stackoverflow.com/questions/56712241/use-of-rest-and-key-at-the-same-time-in-common-lisp