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

时间:2018-10-13 21:52:08

标签: common-lisp

让我们假设我们有一个函数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 的行为。

2 个答案:

答案 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)