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
以列表作为参数。已解决。