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完全?

在此先感谢!

+1

也许我错过了一些微不足道的东西......但是为什么不使用',alist'而不是'(eval alist)'? (事实上​​,你已经在'''之下了,所以完全错过了什么?) –

+0

对不起,忽略我以前的评论。得到它了! –

我不相信你可以避免eval,问题的表述方式:macroexpand let -bindings取决于一个变量。不使用eval的唯一方法是将变量评估推迟到macroexpand之后,但它与扩展绑定的要求相矛盾。我想我说的是对你来说很明显的事情。

所以问题显然是您的要求是否可以改变,以消除eval的需要。这意味着要么避免let -bindings,要么在宏参数中使绑定明确。这取决于你决定是否可以接受,但我个人的看法是 - eval并不如杀死你的用例那样糟糕。我会保留eval那里。 (我最近在Clojure做了基本相同的工作,我需要将相同的本地符号绑定到不同的值,模拟OCaml仿函数。这是一个离题解释为什么我可能会偏向于保持您的工作方式。)

我可能不知道一些elisp特定的技巧 - 虽然即使那样我可能更喜欢eval到我迄今为止从来没有遇到的一些骗局。

+0

谢谢你的包裹!我会标记这一个解决。知道这件事有些令人欣慰,至少没有明显的事情我错过了。如果我为急切的扩展问题找到一个很好的解决方法,我会在这里发布它。 – Phylax

+0

谢谢,看看你如何处理它会很有趣。 –

首先,我会提到急切的宏扩展并没有引入新的问题:如果您尝试字符编译文件,早期的Emacsen会出现相同的问题。

至于避免eval,你可以通过使用其他使用evalcl-progv

(defmacro let-dotted-alist (alist &rest body) 
    (macroexp-let2 nil alist alist 
    `(cl-progv (mapcar #'car ,alist) 
       (mapcar #'cdr ,alist) 
     ,@body))) 

但是请注意,语义是有点不同:变量只能是动态作用域而非词法范围,因为变量的列表将只在运行时是已知的。