【问题标题】:Elisp use apply on progn for list of functionsElisp 在 progn 上使用 apply 以获取函数列表
【发布时间】:2019-11-30 10:33:39
【问题描述】:

我是一个新的 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。所以我安装了邪恶和邪恶的号码。并想要返回 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))


【问题讨论】:

  • 现在有什么问题?为什么(apply 'progn ...) 不起作用?
  • @xuchunyang 因为progn 是“特殊形式”,我认为它是一个函数,但事实并非如此。什么时候在(apply 'progn ...) 上做macroexpand 我会得到(apply '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))
  • 仍然不确定您的问题。您需要一个,因为这是一个 QA 站点,而不是论坛。由于您使用的是宏(实际上您不必这样做,易于阅读/编写的普通 defun 可以正常工作),您不需要applyapply 的目的是,例如,你有一些数字存储在some-numbers 中,想要求和,你使用(apply '+ some-numbers),但是使用宏,你可以只写(cons '+ some-numbers),不需要申请,你可以认为这个宏应该返回一个列表,也就是一个诸如(+ 1 2 3)之类的函数调用,然后它会被自动求值。
  • @xuchunyang 很抱歉让您感到困惑,问题可以澄清一下,为什么apply 不能在progn 上?我认为progn 只是一个函数。所有的“特殊形式”都应该只是一个函数,可以很容易地与其他形式复合。为什么elisp需要“特殊形式”的概念,使其与正常功能区分开来,防止apply在其上。

标签: emacs apply elisp elisp-macro


【解决方案1】:

您不能在“特殊形式”上调用 apply,例如 progn(参见 info (elisp)Special Forms),它们具有独特的评估规则。

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

【讨论】:

  • 我明白了,谢谢。我认为一切都与符号、值、函数和宏有关。现在,我需要深入挖掘一下“特殊形式”。
  • @Xiongchenyu 我认为这确实涵盖了它——除了 lisp 符号被表示为可以同时具有值​​和函数组件的结构——参见例如。 (symbol-function 'progn)(symbol-value 'default-directory),所以你可以有(defun f ())(defvar f "f")
【解决方案2】:

您不需要在宏中使用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)))

【讨论】:

    【解决方案3】:

    不确定您的期望。 progn返回最后一个表格的值,所以4除外,请咨询C-h f progn

    progn 是“C 源代码”中的一种特殊形式。

    (progn 身体...)

    Eval BODY 顺序形成并返回最后一个的值。

    如果你想看到(+ 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 不支持多个返回值。


    (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 提及

    特殊形式和宏在 `apply' 中没有意义。

    【讨论】:

    • 我觉得我的问题不太清楚,所以我只是更新它。
    • 除了在宏中使用 ,@ 将列表展平为 args 以提供 progn。我只是想找到一些其他的方法。
    【解决方案4】:

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

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

    evalgenerally considered 一个初学者程序员的误解,但是在这种情况下你不能使用apply,因为罗夏解释的原因。 Elisp 实际上在这里遵循 Common Lisp 规范,says 代表 apply

    符号作为宏或特殊形式的名称是非法的

    因此,一般来说,您必须在由cons 组装的程序上直接使用eval - 将progn 添加到您的“指令列表”中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-11
      • 2012-11-15
      • 1970-01-01
      • 1970-01-01
      • 2021-12-21
      • 1970-01-01
      • 1970-01-01
      • 2012-02-20
      相关资源
      最近更新 更多