在普通的 lisp 中,它必须是
(apply #'func1 values) ;; since `func1` has to be looked up in function namespace
记住,Clojure 和 Racket/Scheme 是 Lisp1,common 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) 被调用时-
以 lisp 方式 - func2 的参数在进入 func2 的函数体之前都被评估,所以我们最终以 values 作为已评估参数的列表,即((1 2 3) (4) 5)。
所以不知何故,关于最后一个表达式中func1 的参数,我们是一个评估步骤的另类。
但是有一个quote解决方案,我们设法在最后一个表达式中将每个参数提供给func1之前引用每个参数,以“同步” func1 函数调用 - 让参数的评估暂停一轮。
所以我的第一个目标是在 func2 正文中生成一个新的 values 列表,其中每个值列表的参数都被引用(这是在 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))))
这是一种宏(顺便说一句,它创建代码。它组织新代码)但在运行时执行和创建 - 而不是在预编译时。我们使用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)
所以我想说这正是你想要的!