让我们假设我们有一个函数func1
:
(defun func1 (&rest values)
; (do something with values...)
(loop for i in values collect i))
现在,我们有一个函数func2
调用func1
:
(defun func2 (&rest values)
; (do something with values...)
(func1 ???))
我应该用什么代替???
来将func2
的{{1}}的所有参数复制到values
的{{1}}? >
例如,我将具有以下行为:
func1
在an earlier question中,我试图做这样的事情:
values
但是defun无法获取值,因为它不是运行时。在this answer中,他建议我使用(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))))
参数,因此它不能解决我的问题...
如果可能的话,我宁愿避免更改两个函数的原型以及apply
的行为。
答案 0 :(得分:4)
在一般情况下,必须是
(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)
被调用,values
变量为((1 2 3) (4) 5)
但是,当将其拼接为(func1 ,@values)
时,将创建
(func1 (1 2 3) (4) 5)
。但是,如果将其与func2
调用进行比较,
应该是(func1 (list 1 2 3) (list 4) 5)
,这也许是不可能的,因为调用(func2 (list 1 2 3) (list 4) 5)
时-
以一种简洁的方式-func2
的参数在进入func2
的函数体之前都会被评估,因此我们以values
作为已评估参数的列表,即{ {1}}。
以某种方式,关于最后一个表达式中的((1 2 3) (4) 5)
的参数,我们一个评估步骤另类。
但是有一个使用 quote 的解决方案,我们设法在最后一个表达式中将参数提供给func1
之前引用每个参数,以“同步” func1
函数调用-让参数的求值暂停一轮。
因此,我的首要目标是在func1
主体内生成一个新的values
列表,该列表中引用了每个值列表的参数(这是在let绑定中完成的)。
然后最后将这个func2
列表拼接到最后一个表达式:quoted-values
中,对于此类问题/此类调用,它可以等效为(func1 '(1 2 3) '(4) '5)
。
这是通过以下代码实现的:
(func1 (list 1 2 3) (list 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
周围的func1
来查看eval
表达式“展开”的结果-代码- }}:
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
所以我想这正是您想要的!
答案 1 :(得分:3)
列出与传播参数
在Common Lisp中,将列表作为列表而不是作为 spread 参数传递是一种很好的样式:
(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-例如,ABCL(JVM上的Common Lisp)仅允许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)