首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在通用Lisp中使用&rest参数

在通用Lisp中使用&rest参数
EN

Stack Overflow用户
提问于 2019-02-20 09:31:39
回答 1查看 236关注 0票数 3

我在lisp程序(使用Common )中使用&rest参数时遇到了一些问题。

我对语法的了解并不完美,我可能做错了什么。

我有两个职能:

代码语言:javascript
复制
(defun func-one(&rest params) .....)

(defun func-two(param-a param-b &rest params) .....)

他们正在工作,但在某种程度上,在某种程度上,我需要打个电话给功能-第二,像这样:

代码语言:javascript
复制
(func-two value-a value-b params)

我假设我想要的对人类读者来说是清楚的,但是我的语法是错误的,因为我收到了如下错误消息:

代码语言:javascript
复制
*** _ =: (1 2 3 4 5) is not a number

因为"&rest params“的意思是保存一个数字列表,我理解这个信息。但我怎样才能得到我想要的结果呢?

代码语言:javascript
复制
(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呢?而不是我所做的。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2019-02-20 10:53:34

在这个答案中,我将首先解释问题是什么,然后展示解决问题的两种不同方法。

理解问题

从以下两个定义开始:

代码语言:javascript
复制
(defun func-one (&rest params) ...)

因此,func-one是一个接受任意数量的参数(包括零)的函数。在它的正文中,所有参数都将被包装在一个列表中,并绑定到paramsparams的长度将是提供给函数的参数的数量。

代码语言:javascript
复制
(defun func-two (param-a param-b &rest params) ...)

func-two至少有两个参数,并且尽可能多(直到实现限制:对于func-one也是如此)。前两个参数将绑定到param-aparam-b,其余的参数将包装在一个列表中并绑定到params

因此,我们可以编写一个func-two的小假版本,说明它得到了哪些参数:

代码语言:javascript
复制
(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))

现在我们可以用不同的方式来称呼它:

代码语言:javascript
复制
> (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来调用它。

代码语言:javascript
复制
> (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是某种设计问题的症状。要了解这是为什么,请考虑您的函数定义对人类读者意味着什么。

代码语言:javascript
复制
(defun frobnicate (a b &rest things-to-process) ...)

这意味着frobnicate函数的主体中实际上有三个参数(调用它绑定了三个变量),其中第三个参数是要处理的事情列表。但是在用户级别,我想调用它而不必显式地指定列表,所以我将第三个参数指定为&rest,这意味着函数实际上从调用方的角度接受两个或多个参数。

但是,请考虑下面的代码片段:

代码语言:javascript
复制
(apply #'frobnicate this that things-to-process)

这对读这本书的人意味着什么?这意味着您确切地知道要调用哪个函数,frobnicate,并且您已经有了frobnicate实际需要的三个参数:不管前面的两个是什么,然后是要处理的事情列表。但是,由于frobnicate的定义方式,您不能只将它们传递给它:相反,您必须将things-to-process扩展到一组带有apply的单独参数中,然后在调用frobnicate的过程中立即将其展开回(新)列表。好难闻啊。

一般来说,如果您最终不得不使用apply将参数扩展到您编写的函数,并且在编写调用它的代码时您知道它的名称,那么这通常是设计中某种阻抗错配的迹象。

第二种方法:更改设计

因此,问题的第二个答案是改变设计,这样就不会发生这种阻抗错配。解决阻抗不匹配的一种方法是分层定义事物:

代码语言:javascript
复制
(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

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/54782971

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档