我对来自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.
在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荷兰国际集团大量的争论.