Elisp 在 progn 上使用 apply 函数列表

Elisp use apply on progn for list of functions

我是一个新的 emacs 用户。并尝试编写一些 Elisp 来更好地学习这个软件。虽然我遇到了一些问题。

我想在 progn 到 运行 上按顺序使用 apply 函数列表。但我得到如下错误。我只是很困惑,需要一些帮助来告诉我如何在 Elisp 中实现这一点,谢谢。

(progn
  (+ 1 1)
  (+ 2 2)
  )
;; => 4

(apply #'progn ((+ 1 1) (+ 2 2)))
;; => Invalid function: (+ 1 1)

(apply #'progn ('(+ 1 1) '(+ 2 2)))
;; => Invalid function: '(+ 1 1)

(apply #'progn '((+ 1 1) (+ 2 2)))
;; => Invalid function: #<subr progn>

我玩上面的玩具因为我从 vim 来到 emacs。所以我安装了evil和evil number。并想要返回 vim 数字 inc 和 dec 绑定 C-a,C-x。所以我写了一个宏来避免样板文件。

(defmacro set-evil-number-keymap (key-set func &rest modes)
  `(apply 'progn
         ,(-map
          (lambda (mode)
            `(define-key ,(intern (concat "evil-" mode "-state-map")) (kbd ,key-set)
              ',(intern
                 (concat "evil-numbers/" func))))
          ,modes)))

(set-evil-number-keymap "C-a" "inc-at-pt" "normal" "insert")
(set-evil-number-keymap "C-x" "dec-at-pt" "normal" "insert")

我的期望值低于此。虽然它不是 运行.


(progn 
(define-key evil-normal-state-map (kbd "C-a") ’evil-numbers/inc-at-pt) (define-key evil-visual-state-map (kbd "C-a") ’evil-numbers/inc-at-pt))

(progn 
(define-key evil-normal-state-map (kbd "C-x") ’evil-numbers/dec-at-pt) (define-key evil-visual-state-map (kbd "C-x") ’evil-numbers/dec-at-pt))


您不能像 progn 那样在 "special forms" 上调用 apply(请参阅信息 (elisp)特殊形式),它们具有独特的评估规则。

progn 本身已经提供了一种顺序评估机制,因此您的初始尝试确实可以满足您的要求。然而,在顶层,表单无论如何都会按顺序执行,所以那里不需要 progn

不确定您在期待什么。 progn returns最后几格的值,所以4除外,参考C-h f progn:

progn is a special form in `C source code'.

(progn BODY...)

Eval BODY forms sequentially and return value of last one.

如果你想看到(+ 1 1)的结果,你需要请求它,例如

(list (+ 1 1) (+ 2 2))
;; => (2 4)

;; Step 1
(list 2 (+ 2 2))

;; Step 2
(list 2 4)

;; Step 3
(2 4)

函数参数是从左到右求值的。顺便说一下,Emacs Lisp 不支持多个 return 值。


(apply #'progn ((+ 1 1) (+ 2 2)))
;; => Invalid function: (+ 1 1)

由于函数apply的第二个参数是((+ 1 1) (+ 2 2)),Emacs需要计算它的值,计算形式如(foo 1 2 3)foo必须是函数,但 (+ 1 2) 不是函数,它只是一个包含 3 个元素的普通列表。

(apply #'progn ('(+ 1 1) '(+ 2 2)))
;; => Invalid function: '(+ 1 1)

'(+ 1 2),即(quote (+ 1 2))也不是一个函数,它是一个有两个元素的列表。

(apply #'progn '((+ 1 1) (+ 2 2)))
;; => Invalid function: #<subr progn>

progn 不是函数,它是一种特殊形式。 andorletwhile 都是特殊形式,它们就像宏一样,不会急切地计算参数。 (elisp) Calling Functions 提及

special forms and macros do not make sense in `apply'.

您不需要在宏中使用 apply,elisp 有 ,@ 可以将列表展平为参数。所以正确答案是

(defmacro set-evil-number-keymap (key-set func &rest modes)
  `(progn
          ,@(-map
             (lambda (mode)
               `(define-key ,(intern (concat "evil-" mode "-state-map")) (kbd ,key-set)
                  ',(intern
                     (concat "evil-numbers/" func))))
             ,modes)))

你有一点 XY 问题,但对你的名义问题有用的是

(eval (cons 'progn
            '((print (+ 1 1))
              (print (+ 2 2)))))

evalgenerally considered a beginner programmer's misunderstanding, but in this case you just can't use apply for the reasons Rorschach explained. Elisp actually follows the Common Lisp spec here, which says 对于 apply

it is illegal for the symbol to be the name of a macro or special form

所以,一般来说,您必须直接在您通过 consprogn 编译到您的“指令列表”的程序上使用 eval