Emacs Elisp:加载时宏扩展,评估未定义变量

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

我是Lisp新手

我的目标是定义一个宏,使虚线列表的键可以作为变量访问相应的值,因此名为»let domind 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
。我需要它,以便能够将列表作为变量(
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)
?(事实上,你已经在
下了,那么到底缺少了什么?)对不起,忽略我之前的评论。知道了!谢谢你的关心!我会把这个问题解决。知道这一点有点令人欣慰,至少我没有错过任何明显的事情。如果我找到了一个很好的解决急切扩展问题的方法,我会把它贴在这里。谢谢,我很想看看你是如何处理它的。