带有剩余参数的函数调用带有剩余参数的函数

Fni*_*fni 2 common-lisp

让我们假设我们有一个函数func1

(defun func1 (&rest values)
  ; (do something with values...)
  (loop for i in values collect i))
Run Code Online (Sandbox Code Playgroud)

现在,我们有一个函数func2调用func1

(defun func2 (&rest values)
  ; (do something with values...)
  (func1 ???))
Run Code Online (Sandbox Code Playgroud)

我应该放什么而不是???“复制” func2's valuesto func1's 的所有参数values

例如,我会有以下行为:

(func2 1 2 3 4) ; result is (1 2 3 4) and not ((1 2 3 4)).
Run Code Online (Sandbox Code Playgroud)

在之前的一个问题中,我尝试做这样的事情:

(defun func2 (&rest values)
  (macrolet ((my-macro (v)
               `(list ,@v)))
    (func1 (my-macro values))))
Run Code Online (Sandbox Code Playgroud)

但是 defun 无法获取该值,因为它不是运行时。在这个答案中,他建议我使用apply,但是这个函数也需要一个&rest参数,所以它不能解决我的问题......

如果可能,我宁愿避免更改这两个函数的原型,以及func1.

Gwa*_*Kim 5

在普通的 lisp 中,它必须是

(apply #'func1 values) ;; since `func1` has to be looked up in function namespace
Run Code Online (Sandbox Code Playgroud)

记住,Clojure 和 Racket/Scheme 是 Lisp1,常见的 lisp 是 Lisp2。

替代解决方案(只是为了)

我在问自己,如何在没有的情况下完成它apply——只是为了这个。问题在于

`(func2 ,@values)
Run Code Online (Sandbox Code Playgroud)

是,如果例如

 (func2 (list 1 2 3) (list 4) 5)
Run Code Online (Sandbox Code Playgroud)

被调用,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)被调用时 - 以 lisp 方式 -func2在它们进入 的函数体之前,每个参数都被评估func2,所以我们最终得到values作为已评估参数的列表,即((1 2 3) (4) 5).

所以不知何故,关于func1最后一个表达式中的参数,我们是一个评估步骤的另类

但是有一个解决方案的报价,我们管理它给之前引用每个参数func1中最后一个表达式,以“同步”func1函数调用-让争论评价停顿一轮。

所以我的第一个目标是valuesfunc2正文中生成一个新列表,其中引用了每个值列表的参数(这是在 let 绑定中完成的)。然后在最后把这个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))))
Run Code Online (Sandbox Code Playgroud)

这是一种宏(它创建代码顺便说一句。它组织新代码)但在运行时执行和创建 - 而不是在预编译时。使用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)))
Run Code Online (Sandbox Code Playgroud)

如果我们运行它,它会在func2版本中评估之前立即将最后一个表达式作为代码返回:

(func2-1 (list 1 2 3) (list 4) 5)
;; (FUNC1 '(1 2 3) '(4) '5) ;; the returned code
;; the quoted arguments - like desired!
Run Code Online (Sandbox Code Playgroud)

如果我们调用它 using func2(因此对func1所有的评估:

(func2 (list 1 2 3) (list 4) 5) 
;; ((1 2 3) (4) 5)  ;; the result of (FUNC1 '(1 2 3) '(4) '5)
Run Code Online (Sandbox Code Playgroud)

所以我会说这正是你想要的!