Emacs Elisp:加载时宏扩展,评估未定义变量
我是Lisp新手 我的目标是定义一个宏,使虚线列表的键可以作为变量访问相应的值,因此名为»let domind alist«。所以我想要的是:Emacs Elisp:加载时宏扩展,评估未定义变量,emacs,lisp-macros,Emacs,Lisp Macros,我是Lisp新手 我的目标是定义一个宏,使虚线列表的键可以作为变量访问相应的值,因此名为»let domind alist«。所以我想要的是: (setq foo '((a . "aa") (b . "bb"))) (let-dotted-alist foo (message (concat a b))) ==> "aabb" 以下是迄今为止我能想到的最好的: (defmacro let-dotted-alist (alist &a
(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
。我需要它,以便能够将列表作为变量(foo
)而不是文本传递,这是定义函数时的主要用例。要使宏计算出代码,需要已经绑定此变量。我还是到处读到eval的使用倾向于表明代码中存在缺陷?有办法绕过它吗
如果Emacs 24没有引入在加载时希望扩展宏的急切宏扩展,而提供列表的变量(dotlist
,在下面的示例中)仍然无效,那么这将是一个有点学术性的问题:
(defun concat-my-cdrs (dotlist)
(let-dotted-alist dotlist
(print (concat a b))))
根据我对此的计算方式,我可以得到»mapcar:Symbol作为变量的值为void:dotlist«或»Eager macro expansion failure:(void variable dotlist)«。这当然是有意义的,因为变量dotlist在加载时确实是无效的
现在,在我试图找到(本地)禁用急切宏扩展的解决方法之前,是否有任何方法可以改进宏定义以完全避免
eval
?我不相信您可以避免eval
,问题的表述方式是:macroexpand让-绑定取决于变量。不使用eval
的唯一方法是将变量求值推迟到宏扩展之后,但这与扩展let
绑定的要求相矛盾。我想我所说的对你们来说是显而易见的
因此,问题显然是您的需求是否可以更改以消除对eval
的需求。这意味着要么避免let
-绑定,要么在宏参数中显式地进行绑定。由您决定是否可以接受,但我个人的看法是-eval
并没有坏到杀死您的用例。我会把eval
放在那里。(我最近在Clojure做了基本相同的事情,我需要将相同的本地符号绑定到不同的值,模拟OCaml函子。这是一个离题,可以解释为什么我可能倾向于保持您的方式。)
我可能没有意识到一些特定于elisp的技巧——尽管即使如此,我可能更喜欢eval
而不是一些我迄今为止从未遇到过的技巧。首先,我要提到的是,急切的宏扩展并不会带来新问题:如果您尝试对文件进行字节编译,同样的问题也会出现在早期的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)
?(事实上,你已经在,
下了,那么到底缺少了什么?)对不起,忽略我之前的评论。知道了!谢谢你的关心!我会把这个问题解决。知道这一点有点令人欣慰,至少我没有错过任何明显的事情。如果我找到了一个很好的解决急切扩展问题的方法,我会把它贴在这里。谢谢,我很想看看你是如何处理它的。