在 Common Lisp 中同时使用 &rest 和 &key

ore*_*uro 6 lisp common-lisp variadic-functions keyword-argument

我希望同时使用&rest,并&key在同一时间。但是,下面的尝试代码:

(defun test (&rest args &key (name "who")) nil)
(test 1 2 3 4 5 :name "hoge")
Run Code Online (Sandbox Code Playgroud)

导致错误:

*** - 测试: (1 2 3 4 5 :NAME "hoge") 中的关键字参数应该成对出现

当我只给出关键字参数时(test :name "hoge"),它就起作用了。是否可以同时使用 &rest 和 &key?

sds*_*sds 9

的组合&key&rest实际上是非常 常见的Common Lisp中,但几乎总是一起 &allow-other-keys

例如,假设您想为其定义一个包装器 write但不想显式列出它所采用的所有关键字参数:

(defun my-write (object &rest args &key stream &allow-other-keys)
  (write "my wrapper" :stream stream)
  (apply #'write object args))
Run Code Online (Sandbox Code Playgroud)

你会发现许多地方,这种 &rest/ &key/&allow-other-keys 使用模式的任何地方CLOS实际 执行

  • 这是一个很好的观点:根据我的经验,在为 GF 实现方法时,`&allow-other-keys` 通常是一个重要的事情(还有可怕的(IMO)`:allow-other-keys` 关键字参数)。不过,所有这些都需要偶数个参数。 (2认同)

Sim*_*abo 5

在 Common Lisp 的函数定义中,将 rest 参数与关键字参数混合在一起通常不是一个好主意。如果这样做,您可能应该考虑重写函数定义,因为它可能会导致一些意外行为。如果 &rest 和 &key 都出现在参数列表中,那么这两种情况都会发生——所有剩余的值,包括关键字本身,都被收集到一个绑定到 &rest 参数的列表中,并且适当的值也绑定到 &key参数。因此(名称“who”)关键字参数默认绑定到您的其余参数列表。如果您尝试输入参数 (1 2 3 4 5),您将收到错误消息,因为它们未绑定到您的参数(名称“who”)。下面是一个例子:

(defun test (&rest args &key (name "who"))
   (list args name))
Run Code Online (Sandbox Code Playgroud)

在这里,我们有您的函数定义。如果我们尝试调用返回参数列表的函数,我们将在这里看到 &rest 参数绑定到它们的 &key 参数:

CL-USER> (test :name "Davis")
((:NAME "Davis") "Davis")
Run Code Online (Sandbox Code Playgroud)

通过在同一参数列表中混合 &rest 参数和关键字参数,您将无法输入任何与关键字参数不匹配的其余参数,这就是您在此处进入 breakloop 的原因。

现在,如果要创建宏,技术上可以在定义中使用多个参数列表,并在一个列表中添加关键字参数,在另一个列表中添加 &rest(或 &body)参数:

 (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> 
Run Code Online (Sandbox Code Playgroud)