Elisp:在加载时进行宏展开并评估未定义的变量
这里是Lisp-rookie。Elisp:在加载时进行宏展开并评估未定义的变量
我的目标是定义一个宏,使得虚线alist的键可用作变量来访问相应的值,因此名为»let-dotted-alist«。所以,我想的是:
(setq foo '((a . "aa") (b . "bb")))
(let-dotted-alist foo
(message (concat a b)))
==> "aabb"
这是我能想出迄今最好的:
(defmacro let-dotted-alist (alist &rest body)
"binds the car of each element of dotted ALIST to the corresponding cdr and makes them available as variables in BODY."
`(let ,(nreverse
(mapcar
(lambda (p) (list (car p) (cdr p)))
(eval alist)))
,@body))
这里的问题是eval
。我需要它,以便能够将alist作为变量(foo
)而不是文字,这是定义函数时的主要用例。对于宏来确定这个变量需要被绑定的代码。我仍然读到,eval的使用往往表明代码中存在缺陷?有没有办法解决它?
如果Emacs 24没有引入想要在加载时扩展宏的热切宏扩展,那么应该提供alist的变量(dotlist
)仍然是void:
(defun concat-my-cdrs (dotlist)
(let-dotted-alist dotlist
(print (concat a b))))
根据我怎么评价这个,我要么得到»mapcar:Symbol公司作为变量的值是无效的:dotlist«或»渴望宏扩展失败:(空隙变dotlist)«。这当然是有道理的,因为变量dotlist在加载时确实是无效的。
现在,在我试图找到一个解决方法(本地)禁用渴望的宏扩展,是否有任何方法来改善宏定义,以避免eval
完全?
在此先感谢!
我不相信你可以避免eval
,问题的表述方式:macroexpand let
-bindings取决于一个变量。不使用eval
的唯一方法是将变量评估推迟到macroexpand之后,但它与扩展绑定的要求相矛盾。我想我说的是对你来说很明显的事情。
所以问题显然是您的要求是否可以改变,以消除eval
的需要。这意味着要么避免let
-bindings,要么在宏参数中使绑定明确。这取决于你决定是否可以接受,但我个人的看法是 - eval
并不如杀死你的用例那样糟糕。我会保留eval
那里。 (我最近在Clojure做了基本相同的工作,我需要将相同的本地符号绑定到不同的值,模拟OCaml仿函数。这是一个离题解释为什么我可能会偏向于保持您的工作方式。)
我可能不知道一些elisp特定的技巧 - 虽然即使那样我可能更喜欢eval
到我迄今为止从来没有遇到的一些骗局。
谢谢你的包裹!我会标记这一个解决。知道这件事有些令人欣慰,至少没有明显的事情我错过了。如果我为急切的扩展问题找到一个很好的解决方法,我会在这里发布它。 – Phylax
谢谢,看看你如何处理它会很有趣。 –
首先,我会提到急切的宏扩展并没有引入新的问题:如果您尝试字符编译文件,早期的Emacsen会出现相同的问题。
至于避免eval
,你可以通过使用其他使用eval
,cl-progv
:
(defmacro let-dotted-alist (alist &rest body)
(macroexp-let2 nil alist alist
`(cl-progv (mapcar #'car ,alist)
(mapcar #'cdr ,alist)
,@body)))
但是请注意,语义是有点不同:变量只能是动态作用域而非词法范围,因为变量的列表将只在运行时是已知的。
也许我错过了一些微不足道的东西......但是为什么不使用',alist'而不是'(eval alist)'? (事实上,你已经在'''之下了,所以完全错过了什么?) –
对不起,忽略我以前的评论。得到它了! –