常见的lisp:我可以用任意数量的args和可选的关键字args定义一个函数吗?

dsp*_*ate 9 common-lisp

我对来自R和Python的CL相当新.

我想定义一个函数,我可以传递任意数量的参数,并且如果我想要默认值中的不同值,也可以设置我可以设置的默认值的关键字.

在RI中可以这样做:

foo <- function(..., a = 1, b = 2){
    list(a = a, b = b, ...)
}

foo(1, 2, 3)
foo(1, 2, 3, a = 2)
foo(b = 10, 1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

PCL中,它表示你可以组合使用&key和&rest参数但是如果我尝试类似的话,它就不会像我期望的那样工作

(defun foo (&rest rest &key (a 1) (b 2))
   (list rest a b))
Run Code Online (Sandbox Code Playgroud)

在这里,如果我指定除了两个关键字args以外的任何内容,我会收到一个未知和密钥错误:

(foo :a 100 12 3)
>> unknown &KEY argument: 12
Run Code Online (Sandbox Code Playgroud)

我想要的是类似的功能:

(defun bar (&optional (a 1) (b 2) &rest rest)
   (list rest a b))

(bar 5 4 1 2 3 4)
>>((1 2 3 4) 5 4)
Run Code Online (Sandbox Code Playgroud)

但我想选择是否提供参数:a和:b.我正在使用sbcl.

dan*_*lei 7

在CL中没有标准的方法可以做到这一点.rest如果您不想以不同的方式设计API,可以让函数接受参数中的所有内容,并自己解析关键字参数.

也就是说,这里有几点需要进一步探索.它们可能在特定用例中很有用,但相当有限并且利用了依赖于实现的行为.

您可以&allow-other-keys在lambda列表中使用或:allow-other-keys t在调用时使用foo以防止未知键错误,但rest也包括关键字参数的键和值:

CL-USER> (defun foo (&rest rest &key (a 1) (b 2))
           (list rest a b))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3 :allow-other-keys t)
((:A 100 12 3 :ALLOW-OTHER-KEYS T) 100 2)
CL-USER> (defun foo (&rest rest &key (a 1) (b 2) &allow-other-keys)
           (list rest a b))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3)
((:A 100 12 3) 100 2)
Run Code Online (Sandbox Code Playgroud)

正如acelent在下面的评论中正确指出的那样,这可能表示错误.它在默认优化设置下适用于CLISP,SBCL和CCL,但是通过标准,关键字参数名称(即每对参数中的第一个)必须是符号.这是否有效取决于安全级别,并且取决于实现.它应该在安全代码(安全级别为3)中发出错误信号(符合实现).

通常,允许其他键可用于传递关键字参数,但并不完全符合您的要求.一种快速而肮脏的方式可能是过滤关键字参数rest,只是删除它们及其后续元素.像这样的东西:

CL-USER> (defun foo (&rest rest &key (a 1) (b 2) &allow-other-keys)
           (let ((rest (loop for (key value) on rest by #'cddr
                             unless (keywordp key)
                               append (list key value))))
             (list rest a b)))
FOO
CL-USER> (foo)
(NIL 1 2)
CL-USER> (foo :a 100 12 3)
((12 3) 100 2)
Run Code Online (Sandbox Code Playgroud)

正如coredump在他的回答中所指出的那样,对于偶数个论证而言,标准只会起作用.(对于某些安全级别,它可能在某些实现下使用奇数个参数,但它在我测试的实现中不起作用.)此外,显然它在其他方面不健壮(关键字参数的不同位置等). ),并不是用于生产用途,而只是作为可能的探索的起点.

正确的解决方案包括编写自己的关键字参数解析器,并使用它rest,或者如coredump的答案中所指出的,使用不同的API.值得一提的另一点是,在CL中,apply大量的参数通常不是一个好主意,因为它可能导致低效的代码.更糟糕的是,它也不是很可靠,因为允许的参数数量与实现有关.例如,在我的系统上的CCL下,call-arguments-limit是65536.在其他实现和系统下,它可能显着 - 甚至数量级 - 更小.因此,在一般情况下,宁愿reduce荷兰国际集团向apply荷兰国际集团大量的争论.