gpt4 book ai didi

lisp - 在 Common Lisp 中使用 &rest 参数

转载 作者:太空宇宙 更新时间:2023-11-03 18:41:40 24 4
gpt4 key购买 nike

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

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

我有两个功能:

(defun func-one(&rest params) .....)

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

他们正在工作,但在某些时候,在 func-one 中,我需要调用 function-2,如下所示:
(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 的正确方法是什么?而不是我所做的。

最佳答案

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

理解问题

首先查看两个定义:

(defun func-one (&rest params) ...)

所以, func-one是一个接受任意数量参数(包括零)的函数。在其主体内,所有参数都将被包装在一个列表中,并将绑定(bind)到 params : params 的长度将是提供给函数的参数数量。
(defun func-two (param-a param-b &rest params) ...)
func-two至少需要两个参数,并且任意数量(直到实现限制: func-one 也是如此)。前两个参数将绑定(bind)到 param-aparam-b , 所有其余参数将被包装在一个列表中并绑定(bind)到 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是一个非常不错的技术答案(关于 arglist 长度和性能存在一些问题,但我认为这些是次要的)。但是,通常情况下必须使用 apply这种方式是某种设计问题的征兆。要了解为什么会这样,请考虑您的函数定义对人类读者意味着什么。
(defun frobnicate (a b &rest things-to-process) ...)

这意味着 frobnicate函数在其主体中确实有三个参数(调用它会绑定(bind)三个变量),其中第三个参数是要处理的一些事物列表。但是在用户级别我想调用它而不必明确指定该列表,所以我将第三个参数指定为 &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 .

关于lisp - 在 Common Lisp 中使用 &rest 参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54782971/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com