Common lisp 具有rest参数的函数调用具有rest参数的函数

Common lisp 具有rest参数的函数调用具有rest参数的函数,common-lisp,Common Lisp,假设我们有一个函数func1: (defun func1 (&rest values) ; (do something with values...) (loop for i in values collect i)) (defun func2 (&rest values) ; (do something with values...) (func1 ???)) 现在,我们有一个函数func2,它调用func1: (defun func1 (&rest

假设我们有一个函数
func1

(defun func1 (&rest values)
  ; (do something with values...)
  (loop for i in values collect i))
(defun func2 (&rest values)
  ; (do something with values...)
  (func1 ???))
现在,我们有一个函数
func2
,它调用
func1

(defun func1 (&rest values)
  ; (do something with values...)
  (loop for i in values collect i))
(defun func2 (&rest values)
  ; (do something with values...)
  (func1 ???))
我应该用什么代替
func2
值的所有参数“复制”到
func1

例如,我会有以下行为:

(func2 1 2 3 4) ; result is (1 2 3 4) and not ((1 2 3 4)).
在这方面,我试着做如下的事情:

(defun func2 (&rest values)
  (macrolet ((my-macro (v)
               `(list ,@v)))
    (func1 (my-macro values))))
但是defun无法获取该值,因为它不是运行时。在中,他建议我使用
apply
,但是这个函数也需要
&rest
参数,所以它不能解决我的问题


如果可能的话,我宁愿避免更改这两个函数的原型,而在common lisp中
func1

的行为必须是

(apply #'func1 values) ;; since `func1` has to be looked up in function namespace
请记住,Clojure和Racket/Scheme是Lisp1,而公共lisp是Lisp2

替代解决方案(仅为方便)

我在问自己,如何在没有
apply
的情况下完成这项工作?仅仅是为了这个目的。 问题在于

`(func2 ,@values)
是的,如果

 (func2 (list 1 2 3) (list 4) 5)
调用时,
变量为
((1 2 3)(4)5)
但是当它被拼接到
(func1,@values)
中时,创建的是
(函数1(1 2 3)(4)5)
。但如果我们将其与
func2
调用进行比较, 它应该是
(func1(list 1 2 3)(list 4)5)
,这可能是不可能的,因为当调用
(func2(list 1 2 3)(list 4)5)
时- 在lisp方式下,
func2
的参数在进入
func2
的函数体之前都要进行求值,因此我们最终得到了
值作为已求值参数的列表,即
((1 2 3)(4)5)

因此,不知何故,关于上一个表达式中
func1
的参数,我们是一个计算步骤

但是quote有一个解决方案,即在最后一个表达式中将参数赋给
func1
之前,我们设法引用每个参数,以同步
func1
函数调用-让参数的计算暂停一轮

因此,我的第一个目标是在
func2
主体中生成一个新的
values
列表,其中引用了每个values列表的参数(这是在let绑定中完成的)。 然后在末尾将这个
引用值
列表拼接到最后一个表达式:
(func1'(1 2 3)-(4)5)
,对于此类问题/此类调用,它可以被视为等价于
(func1(1 2 3)(1 4)5)
。 这是通过以下代码实现的:

(defun func2 (&rest vals)
  (let ((quoted-values (loop for x in vals
                                     collect `',x)))
    ; do sth with vals here - the func2 function -
    (eval `(func1 ,@quoted-values))))
这是一种宏(它创建代码,顺便说一句,它组织新代码),但在运行时执行和创建,而不是在预编译时。我们使用
eval
动态执行生成的代码

就像
macroexpand-1
,我们可以通过移除
eval
来查看结果-
func1
表达式“展开”的代码-,我称之为
func2-1

(defun func2-1 (&rest vals)
  (let ((quoted-values (loop for x in vals
                                     collect `',x)))
    ; do sth with vals here - the func2 function -
    `(func1 ,@quoted-values)))
如果我们运行它,它会在
func2
版本中对最后一个表达式进行求值之前立即以代码形式返回最后一个表达式:

(func2-1 (list 1 2 3) (list 4) 5)
;; (FUNC1 '(1 2 3) '(4) '5) ;; the returned code
;; the quoted arguments - like desired!
如果我们使用
func2
调用它,就会发生这种情况(因此,对
func1
all的求值:

(func2 (list 1 2 3) (list 4) 5) 
;; ((1 2 3) (4) 5)  ;; the result of (FUNC1 '(1 2 3) '(4) '5)

所以我想说,这正是您想要的!

列表与扩展参数

在Common Lisp中,最好将列表作为列表而不是扩展参数传递:

(foo (list 1 2 3))   ; better interface

(foo 1 2 3)          ; interface is not so good
该语言的定义方式是编译器可以使用有效的函数调用,这意味着可以传递给函数的参数数量是有限的。有一个标准变量可以告诉我们特定实现支持多少个参数:

这是我的Mac上的LispWorks:

CL-USER 13 > call-arguments-limit
2047
有些实现允许更多的参数,但这个数字可以低至50个——例如,JVM上的公共Lisp ABCL只允许50个参数

使用参数列表进行计算

但有时我们希望参数作为一个列表,然后我们可以使用
&rest
参数:

(lambda (&rest args)
  (print args))
这稍微有点效率,因为参数将使用一个列表。通常Lisp会尽量避免使用参数列表,如果可能的话,它们将在寄存器或堆栈中传递

如果我们知道参数列表将不被使用,那么我们可以给编译器一个使用堆栈分配的提示-如果可能的话:

(lambda (&rest args)
  (declare (dynamic-extent args))
  (reduce #'+ args))
在上面的函数中,参数列表可以在离开函数时解除分配,因为此时不再使用参数列表

如果要将这些参数传递给另一个函数,可以使用
FUNCALL
,通常更有用的
APPLY

(lambda (&rest args)
  (funcall #'write (first args) (second args) (third args)))
或更有用:

(lambda (&rest args)
  (apply #'write args))
还可以在要应用的列表之前向
APPLY
添加其他参数:

CL-USER 19 > ((lambda (&rest args)
                (apply #'write
                       (first args)               ; the object
                       :case :downcase            ; additional args 
                       (rest args))
                (values))
              '(defun foo () 'bar)
              :pretty t
              :right-margin 15)
(defun foo ()
  'bar)

它有效,谢谢!:D我很惭愧,我创建了
funcall
apply
并忘记了
apply
以列表作为参数。已解决。