我在lisp程序(使用Common )中使用&rest参数时遇到了一些问题。
我对语法的了解并不完美,我可能做错了什么。
我有两个职能:
(defun func-one(&rest params) .....)
(defun func-two(param-a param-b &rest params) .....)他们正在工作,但在某种程度上,在某种程度上,我需要打个电话给功能-第二,像这样:
(func-two value-a value-b params)我假设我想要的对人类读者来说是清楚的,但是我的语法是错误的,因为我收到了如下错误消息:
*** _ =: (1 2 3 4 5) is not a number因为"&rest params“的意思是保存一个数字列表,我理解这个信息。但我怎样才能得到我想要的结果呢?
(1 2 3 4 5) passed in the "&rest params" should be seen as : 1 2 3 4 5 by func-two
and not as one element which is a list (indeed not a number).那么,怎样才是正确的方法来称呼Func-2呢?而不是我所做的。
发布于 2019-02-20 10:53:34
在这个答案中,我将首先解释问题是什么,然后展示解决问题的两种不同方法。
理解问题
从以下两个定义开始:
(defun func-one (&rest params) ...)因此,func-one是一个接受任意数量的参数(包括零)的函数。在它的正文中,所有参数都将被包装在一个列表中,并绑定到params:params的长度将是提供给函数的参数的数量。
(defun func-two (param-a param-b &rest params) ...)func-two至少有两个参数,并且尽可能多(直到实现限制:对于func-one也是如此)。前两个参数将绑定到param-a和param-b,其余的参数将包装在一个列表中并绑定到params。
因此,我们可以编写一个func-two的小假版本,说明它得到了哪些参数:
(defun func-two (param-1 param-2 &rest params)
(format t "~&~D arguments~%param-1: ~S~%param-2: ~S~%params: ~S~%"
(+ 2 (length params))
param-1 param-2 params)
(values))现在我们可以用不同的方式来称呼它:
> (func-two 1 2)
2 arguments
param-1: 1
param-2: 2
params: nil
> (func-two 1 2 3)
3 arguments
param-1: 1
param-2: 2
params: (3)
> (func-two 1 2 3 4)
4 arguments
param-1: 1
param-2: 2
params: (3 4)
> (func-two 1 2 '(3 4))
3 arguments
param-1: 1
param-2: 2
params: ((3 4))问题是:如果您已经将一堆东西打包到一个列表中,那么当您调用func-two时,该列表只是函数的一个参数:它们不是“所有其他参数”--这正是您想要的。
有两种解决这个问题的方法,在下面的章节中描述。
第一种方法:使用apply
第一个也是最简单的方法是使用apply,它在生活中的目的就是处理这个问题:如果您想用列表中的一堆参数调用一个函数,以及一些您单独拥有的主要参数,那么您可以使用apply来调用它。
> (apply #'func-two 1 2 '(3 4))
4 arguments
param-1: 1
param-2: 2
params: (3 4)
> (apply #'func-two '(1 2 3 4))
4 arguments
param-1: 1
param-2: 2
params: (3 4)您可以看到apply正在做什么:它的第一个参数是要调用的函数,它的最后一个参数是‘所有其余的参数’,而在这些参数之间的任何参数都是一些领先的参数。
这个答案的其余部分讨论的是设计,这必然是一个意见问题。
使用apply是一个完美的技术解决方案(讨论列表的长度和性能存在一些问题,但我认为这些问题在这里是次要的)。然而,通常情况是,必须以这种方式使用apply是某种设计问题的症状。要了解这是为什么,请考虑您的函数定义对人类读者意味着什么。
(defun frobnicate (a b &rest things-to-process) ...)这意味着frobnicate函数的主体中实际上有三个参数(调用它绑定了三个变量),其中第三个参数是要处理的事情列表。但是在用户级别,我想调用它而不必显式地指定列表,所以我将第三个参数指定为&rest,这意味着函数实际上从调用方的角度接受两个或多个参数。
但是,请考虑下面的代码片段:
(apply #'frobnicate this that things-to-process)这对读这本书的人意味着什么?这意味着您确切地知道要调用哪个函数,frobnicate,并且您已经有了frobnicate实际需要的三个参数:不管前面的两个是什么,然后是要处理的事情列表。但是,由于frobnicate的定义方式,您不能只将它们传递给它:相反,您必须将things-to-process扩展到一组带有apply的单独参数中,然后在调用frobnicate的过程中立即将其展开回(新)列表。好难闻啊。
一般来说,如果您最终不得不使用apply将参数扩展到您编写的函数,并且在编写调用它的代码时您知道它的名称,那么这通常是设计中某种阻抗错配的迹象。
第二种方法:更改设计
因此,问题的第二个答案是改变设计,这样就不会发生这种阻抗错配。解决阻抗不匹配的一种方法是分层定义事物:
(defun frobnicate (a b &rest things-to-process)
;; user convenience function
(frob a b things-to-process))
(defun frob (a b things-to-process)
;; implementation
...)现在,在已经得到要处理的对象列表的实现中,调用frob而不是frobnicate,不再需要使用apply。
https://stackoverflow.com/questions/54782971
复制相似问题