3

Lisp 新手在这里。

我的目标是定义一个宏,它将使点列表的键可用作变量以访问相应的值,因此名称为»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 没有引入想要在加载时扩展宏的急切宏扩展,这将是一个有点学术问题,dotlist而应该提供 alist 的变量(在以下示例中)仍然是无效的:

(defun concat-my-cdrs (dotlist)
  (let-dotted-alist dotlist
            (print (concat a b))))

根据我对此的评估方式,我要么得到»mapcar:符号作为变量的值是 void:dotlist«,要么得到 »Eager 宏扩展失败:(void-variable dotlist)«。这当然是有道理的,因为变量 dotlist 在加载时确实是无效的。

现在,在我尝试找到(本地)禁用急切宏扩展的解决方法之前,有没有办法改进宏定义以eval完全避免这种情况?

4

2 回答 2

1

首先,我要提到 eager-macroexpansion 不会引入新问题:如果您尝试对文件进行字节编译,同样的问题也会出现在早期的 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)))

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

于 2016-09-09T13:28:46.870 回答
0

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

所以问题显然是您的要求是否可以更改以消除对eval. 这意味着要么避免let绑定,要么在宏参数中明确绑定。由您来决定它是否可以接受,但我个人的看法是 -eval还不错到杀死您的用例。我会eval留在那里。(我最近在 Clojure 中做了同样的事情,我需要将相同的局部符号绑定到不同的值,模拟 OCaml 函子。这是一个题外话,可以解释为什么我可能偏向于保持你的做法。)

我可能不知道一些 elisp 特定的技巧 - 尽管即使那样我也可能更喜欢eval一些我迄今为止从未遇到过的技巧。

于 2016-09-08T01:07:07.167 回答