我刚刚开始写这个函数,我想知道是否有办法,如果只输入& key参数,可以忽略&可选列表。
(defun test (&optional arg (i 0) &key (size s))
...)
我希望能够运行
(test arg)
或
(test arg i)
但也
(test :size)
现在这是一个更好的模拟,但我不知道放在哪里:大小在参数列表中
(defun test (&optional arg (i 0))
(cond ((eq arg nil) (return-from test (test-1)))
((listp arg)
(return-from test (test-2 arg)))
((pointerp arg) (mem-aref (test-3 arg) :int i))
(:size (size-test arg))
(t nil)))
so i can run (test) and get:
<output of (test-1)>
I can run (test '(1 2 3)) and get:
<output of (test-2 arg)>
I can run (test <pointer> 0)
and output is:
<output of (mem-aref (test-3 arg) :int i)>
I can run (test :size) and get:
<output of (test-size arg)>
答案 0 :(得分:6)
您必须使用&rest
参数列表并在函数中处理它。
应避免混合使用可选和关键字参数。使用可选和关键字参数是BAD风格。这是使用少数函数的无数错误的来源(如READ-FROM-STRING
)。
答案 1 :(得分:6)
混合可选和关键字参数仍然不是那么容易做到的事情。如果函数接受可选参数,那么除非还提供了可选参数,否则您将无法使用关键字参数。否则,第一个关键字将被解释为可选参数,依此类推。例如,请参阅此Stack Overflow问题:How can I have optional arguments AND keyword arguments to the same function?。正如该问题的答案所指出的那样,混合可选和关键字参数通常是容易出错的做法。 Common Lisp用read-from-string
来做,它经常导致人们陷入困境。
你提出的建议不仅仅是有一个同时使用关键字和可选参数的函数,但是从它的声音来看,它实际上正在检查参数的类型,并采取一种行为。一个案件,另一个案件。在这种情况下,如果i
应该是一个数字,那么你可以检查第一个参数,如果它是一个数字,则将其视为可选参数,其余作为关键字参数,如果不是一个数字,然后将整个列表视为关键字参数。您可以使用以不同方式进行结构化的&rest
参数来执行此操作:
(defun frob (&rest args)
(flet ((frob-driver (i size)
(list i size)))
(if (or (endp args) (numberp (first args)))
;; no args, or the first argument is a number (and thus
;; not a keyword argument)...
(destructuring-bind (&optional (i 'default-i) &key (size 'default-size)) args
(frob-driver i size))
;; otherwise, there are some non-numeric arguments at
;; beginning, so it must be the keyword list, and that the
;; "optional" wasn't provided.
(destructuring-bind (&key (size 'default-size) &aux (i 'default-i)) args
(frob-driver i size)))))
(frob 10 :size 50) ; give i and size
;=> (10 50)
(frob :size 60) ; give size, but not i
;=> (default-i 60)
(frob 40) ; give i, but not size
;=> (40 default-size)
(frob) ; give neither
;=> (default-i default-size)
在评论中,您提到您希望能够在参数列表中使用非关键字符号作为关键字。这很容易。在HyperSpec中,§3.4.1 Ordinary Lambda Lists描述了关键字参数的语法:
[&key {var | ({var | (keyword-name var)} [init-form [supplied-p-parameter]])}* [&allow-other-keys]]
这意味着您可以定义如下函数:
(defun frob (&key foo ((bar-keyword bar-variable) 'default-baz))
(list foo bar-variable))
(frob :foo 1 'bar-keyword 2)
;=> (1 2)
(frob :foo 3)
;=> (3 default-baz)
(frob 'bar-keyword 2)
;=> (nil 2)
答案 2 :(得分:0)
简而言之,&#34; no&#34;。没有办法实现这一点。作为一般规则,请尽量避免混合&rest
,&optional
和&key
参数,因为它们的互动很微妙,并且会更频繁地绊倒你而不是实际有用。
此外,如果:size
是关键字参数,则(test :size)
缺少一个参数(绑定size
的值)。您最好的选择可能是arg
,看看它是:size
还是其他。
答案 3 :(得分:0)
发布答案,因为我解决了这里的主要问题是我想出的结果代码,cond语句是什么问题。 &amp; args用法创建了另一个问题,正在讨论该帖子here。 ((symbolp (cadr args)) (%vector-float-size (first args)))
这句话就是我从约书亚·泰勒(Joshua Taylors)那里得到的,是一本善意的文章和非常有用的答案。
(defun vector-float (&rest args)
(cond ((eq (first args) nil) (return-from vector-float (%vector-float)))
((listp (first args))
(c-arr-to-vector-float (first args)))
((symbolp (cadr args)) (%vector-float-size (first args)))
((pointerp (first args)) (mem-aref (%vector-float-to-c-array (first args)) :float (second args)))
(t nil)))