Macro 内部的 Loop 应该怎么用?

有这么一个常用的FORM

(let ((map (make-sparse-keymap)))
  (define-key map [mode-line mouse-3] #'test1)
  (define-key map [mode-line mouse-1] #'test2)
  map)

我希望可以这么写:

(make-sparse-keymap-local
 ([mode-line mouse-1] . #'test1)
 ([mode-line mouse-3] . #'test2))

于是定义了一个这样的宏,但是报错:(invalid-function [mode-line mouse-3])

(defmacro make-sparse-keymap-local (&rest keymaps)
  `(let ((map (make-sparse-keymap)))
     (loop for (key . value) in ,@keymaps do
           (define-key map key value))
     map))

loop 无关。你 backquote 的用法有问題。

(defmacro make-sparse-keymap-local (&rest keymaps)
  `(let ((map (make-sparse-keymap)))
     (loop for (key . value) in ',keymaps
           do (define-key map key value))
     map))

macroexpand-1 好好了解一下。


实际上用 loop 可以直接省去 let

(defmacro make-sparse-keymap-local (&rest keymaps)
  `(loop with map = (make-sparse-keymap)
         for (key . value) in ',keymaps
         do (define-key map key value)
         finally (return map)))
1 个赞

:+1: 这个第一次知道

(defmacro make-sparse-keymap-local (&rest bindings)
  (let ((map (make-symbol "map")))
    `(let ((,map (make-sparse-keymap)))
       ,@(cl-loop for (key . cmd) in bindings
                  collect `(define-key ,map ,key ,cmd))
       ,map)))

(macroexpand '(make-sparse-keymap-local
               ([mode-line mouse-1] . #'test1)
               ([mode-line mouse-3] . #'test2)))
;; =>
(let
    ((map
      (make-sparse-keymap)))
  (define-key map
    [mode-line mouse-1]
    #'test1)
  (define-key map
    [mode-line mouse-3]
    #'test2)
  map)

(make-sparse-keymap-local
 ([mode-line mouse-1] . #'test1)
 ([mode-line mouse-3] . #'test2))
;; => (keymap (mode-line keymap (mouse-3 . test2) (mouse-1 . test1)))

其實吧,直接这樣就行了。

(defmacro make-sparse-keymap (&rest keymaps)
  (loop with map = (make-sparse-keymap)
        for (key . value) in keymaps
        do (define-key map (eval key) (eval value))
        finally (return `',map)))

(make-sparse-keymap-local ([mode-line mouse-1] . #'test1)
                          ([mode-line mouse-3] . #'test2))

;; => (keymap
;;    (mode-line keymap
;;               (mouse-3 . test2)
;;               (mouse-1 . test1)))

是的,这个特别完美~生成的代码和手写的一摸一样

@LdBeth 你这个也太魔性了

反正 Emacs Lisp 里面数据主要都是列表。

你这样不如用函数:

(defun make-sparse-keymap-local (&rest bindings)
  (cl-loop with map = (make-sparse-keymap)
           for (key . cmd) in bindings
           do (define-key map key cmd)
           finally return map))

macro byte compile 后減少开銷。