elisp macro的应用场景问题

我有两个函数有一段重复代码,我想把这段代码抽象出来。

这段代码的作用是解析变量package,得到两个函数变量package-symboreciepe

一般来说都是写一个函数,package进去,package-symboreciepe出来。

不过我还想到一个用macro的方法,大概是这样:

(defmacro with-recipe (&rest body)
  `(let ((package-symbol (something using package))
         (recipe (something using package)))
       ,@body))

这个方法看起来就很违规很危险,但是我不太明白具体为什么。我能想到的是用到的变量没有显式的表现出来(当然我在文档里都写清楚了)。是这样吗?

这样用没啥问题啊, macro 的作用就是去掉重复代码和嵌套函数

吹毛求疵一点就是没用 gensym 其它没啥问题,不过 Emacs 也没像样的 gensym 可以用。

gensym不是避免body內变量撞车用的么?(我没理解错的话)但是我的目的就是让body里的代码可以用package-symbolreciepe啊。还是说是别的地方?

为什麼不用 flet

package 为什麼不在 argilst 里?

抱歉我打错了……已经在问题里修正

我问的就是这个地方,把package加进arglist里在下面就要各种quote,很麻烦而且容易错,我这样的话body里面和(something using package)package就很方便。但是我不清楚这么做会不会出什么问题。

内置的gensymmake-symbol不能用吗?感觉还行啊

因为 Emacs 实现的问题多出来一些限制。

https://lists.gnu.org/archive/html/help-gnu-emacs/2010-05/msg00023.html

好像大部分场合都能应付了…有什么可能出问题的刁钻场合么?

(make-symbol "f") ;; f
;; CL
(make-symbol "F") ;; #:F 

区别多了去了,比如如果要打印出来再读回去,Elisp 完全不会区分 interned 和 uninternd

今天发现可以让emacs在打印的时候对gensym额外处理的

ELISP> (let ((print-gensym t))
         (prin1-to-string `(let ((m ,(make-symbol "m"))))))
"(let ((m #:m)))"
ELISP> (let ((print-gensym nil))
         (prin1-to-string `(let ((m ,(make-symbol "m"))))))
"(let ((m m)))"

gensym不是serializable的,按额外的格式打印出来然后read回去,每个gensym literal都会重新分配一个新的gensym,这意味着生成的sexp里所有gensym都不一样,所以并没有什么卵用

(eq #1='#:foo #1#) ;; always t

(setq print-gensym t
      print-circle t)

(let ((s (gensym)))
  (print (list s s)))
;; (#1=#:g7 #1#)
2 个赞