【发布时间】:2015-04-12 16:11:17
【问题描述】:
我正在为宏 lambda 列表尝试不同的绑定模型。
编辑:事实上,我的测试宏的 lambda 列表总是(&rest ...)。这意味着我正在“解构”参数列表而不是 lambda 列表。我试图获得一个解决方案,可以将可选与关键参数或rest/body 与关键参数结合起来——这两种组合在 Common Lisp 标准实现中都不起作用。
所以我有不同的函数给我一个绑定列表,这些绑定的语法与 'let' 使用的语法相同。
例如:
(build-bindings ...) => ((first 1) middle (last "three"))
现在我想在我的测试宏中使用一个简单的宏,将这样的列表提供给“让”。
如果我有一个文字列表,这很简单:
(defmacro let-list (_list &rest _body)
`(let ,_list ,@_body))
(let-list ((a 236)) a) => 236
但这与简单的“让”相同。
我想要的是与生成的列表相同的东西。
例如
(let-list (build-bindings ...)
(format t "first: ~s~%" first)
last)
使用(build-bindings ...),在与调用(let-list ...) 相同的词法范围内求值,返回
((first 1) middle (last "three"))
宏的扩展应该是
(let
((first 1) middle (last "three"))
(format t "first: ~s~%" first)
last)
应该打印1并返回"three"。
知道如何实现吗?
编辑(使问题更笼统):
如果我有一个 (symbol value) 对的列表,即 let 对其绑定列表所需的相同语法,例如((one 1) (two 'two) (three "three")),有没有办法编写一个宏来创建符号的词法绑定,并为其&rest/&body 参数提供值?
这似乎是 Joshua 向我指出的一个可能的解决方案:
(let ((list_ '((x 23) (y 6) z)))
(let
((symbols_(loop for item_ in list_
collect (if (listp item_) (car item_) item_)))
(values_ (loop for item_ in list_
collect (if (listp item_) (cadr item_) nil))))
(progv symbols_ values_
(format t "x ~s, y ~s, z ~s~%" x y z))))
evaluates to:
;Compiler warnings :
; In an anonymous lambda form: Undeclared free variable X
; In an anonymous lambda form: Undeclared free variable Y
; In an anonymous lambda form: Undeclared free variable Z
x 23, y 6, z NIL
我还可以轻松地重新排列我的 build-bindings 函数以返回所需的两个列表。
一个问题是,如果变量从未被声明为特殊的,编译器会发出警告。
另一个问题是,如果动态绑定的变量也用在周围的词法绑定中,它们会被词法绑定所掩盖——如果它们从未被声明为特殊的:
(let ((x 47) (y 11) (z 0))
(let ((list_ '((x 23) (y 6) z)))
(let
((symbols_(loop for item_ in list_
collect (if (listp item_) (car item_) item_)))
(values_ (loop for item_ in list_
collect (if (listp item_) (cadr item_) nil))))
(progv symbols_ values_
(format t "x ~s, y ~s, z ~s~%" x y z)))))
evaluates to:
x 47, y 11, z 0
更好的方法可能是:
(let ((x 47) (y 11) (z 0))
(locally
(declare (special x y))
(let ((list_ '((x 23) (y 6) z)))
(let
((symbols_(loop for item_ in list_
collect (if (listp item_) (car item_) item_)))
(values_ (loop for item_ in list_
collect (if (listp item_) (cadr item_) nil))))
(progv symbols_ values_
(format t "x ~s, y ~s, z ~s~%" x y z))))))
evaluates to:
;Compiler warnings about unused lexical variables skipped
x 23, y 6, z NIL
我目前看不出动态progv绑定是否还有其他问题。
但是 progv 的整个辣酱玉米饼馅包裹在 locally 中,所有的符号都被声明为宏的特殊呼叫 - 由于同样的原因,这又是不可能的 let-list 不起作用:(
可能是一种我不知道的macro-lambda-list destructuring-hook。
我必须研究destructuring-bind 的实现,因为那个宏可以做我想做的事情。也许这会启发我;)
【问题讨论】:
-
您知道不能在运行时轻松生成绑定列表吗?想象一下编译器看到代码而不执行它。它需要在编译时运行
build-bindings... -
build-bindings实际上是在编译时执行的,因为它是用于宏内部而不是函数内部,所以以自定义方式“解构”参数所需的所有信息都存在于编译时 -假设我对这个假设没有错。问题是如何将build-binding的结果插入到运行时let。 -
请展示您希望能够编写的代码的具体示例,以及它应该扩展到的代码。使用简写
(let-list (bindings... ) ...)并不能告诉我们您实际期望用什么来代替(bindings... )。 -
我已经阅读了这个问题,但仍然不清楚你想要什么。我们是否应该将
(build-bindings ...)视为返回((first 1) middle (last "three"))的函数?如果是这样,我认为你想要的不可能以你想要的方式实现。 -
@carpetemporem 是的,但是 build-bindings 的 arguments 是什么?有什么东西可以让我们在编译时知道变量是
first、middle和last,它们的值是1、@987654357 @ 和"three"?如果我们要帮助您解决这个问题,这对我们来说是非常重要的信息。 “有没有办法编写一个宏来创建符号的词法绑定与它的 &rest/&body 参数提供的值?”的答案?绝对是是的,但是您需要能够在宏扩展时间获得这些符号。没有
标签: macros common-lisp