如何通过遍历列表创建多个defun?


11

我正在优化我的emacs配置,可以为列表中的所有主题动态创建交互式功能。

以下是我尝试制作的结构的简化版本。

;; List containing names of functions that I want to create
(setq my/defun-list '(zz-abc
                      zz-def
                      zz-ghi))

;; Elisp macro to create an interactive defun whose name
;; is passed as the macro argument
(defmacro my/create-defun (defun-name)
  `(defun ,defun-name ()
     (interactive)
     (let ((fn-name (symbol-name ',defun-name)))
       (message "Testing creation of function %s" fn-name))))

;; Loop to call the above macro for each element in the list
;; DOES *NOT* WORK
(dolist (name my/defun-list)
  (my/create-defun name))

但是,如果我手动展开循环,它将起作用:

;; WORKS
(my/create-defun zz-abc)
(my/create-defun zz-def)
(my/create-defun zz-ghi)

但是以下内容在我传递符号名称的地方不起作用(这可能是循环自行展开时发生的情况)。注意宏参数之前的引号。

;; DOES *NOT* WORK
(my/create-defun 'zz-abc)
(my/create-defun 'zz-def)
(my/create-defun 'zz-ghi)

更新资料

感谢@wvxvw的帮助,我终于可以正常工作了

正如@wvxvw所建议的那样,我不会为任何用例批量生成defun。这是一个特殊的用例,对于一个名为的主题XYZ,我想生成一个defun load-theme/XYZ来完成以下任务:

  • 禁用所有其他可能处于活动状态的主题
  • 呼唤load-themeXYZ
  • 做更多与该主题相关的自定义工作;我通过列表为每个主题传递自定义设置my/themes

1
把所有都defuns放进去prognprogn允许为顶级形式(在某种意义上,适用于顶级形式的所有内容progn也适用于其内容)。但是我会质疑以这种方式创建函数的原理:为什么不拥有一个以lambda作为值的has-table?
wvxvw

@wvxvw我不明白这个建议。我只有一个defun创建宏,我想在一个循环中多次调用它。手动展开的示例旨在显示在我尝试找出此问题时有效的方法和无效的方法。我的目标是拥有列表而不是列表,并为各种主题创建交互功能。目前cons,列表仅由es 组成,但我计划将其转换为具有每个主题的自定义属性的列表。
Kaushal Modi 2015年

好吧,您调用了(my/create-defun name)3次,因此您应该定义一个称为name3次的函数。
奥马尔(Omar)

Answers:


13

这是尝试进行解释的建议。

(defmacro my/create-defun (defun-name)
  `(defun ,defun-name ()
     (interactive)
     (let ((fn-name (symbol-name ',defun-name)))
       (message "Testing creation of function %s" fn-name))))

(dolist (name my/defun-list)
  ;; Macros are meant to create code, not execute it.  Think
  ;; about simply substituting the contents of your macro here
  ;; what would you expect it to do?
  (my/create-defun name))

(dolist (name my/defun-list)
  ;; This is not something a compiler (or interpreter)
  ;; can work with, it needs all sources of the code it
  ;; is going to execute
  (defun defun-name ()
    (interactive)
    (let ((fn-name (symbol-name 'defun-name)))
      (message "Testing creation of function %s" fn-name))))

;; This works because you, indeed created three defuns
(my/create-defun zz-abc)
(my/create-defun zz-def)
(my/create-defun zz-ghi)

;; This doesn't work because `defun' macro expect the name of
;; the function to be a symbol (but you are giving it a list
;; `(quote zz-abc)'.
(my/create-defun 'zz-abc)
(my/create-defun 'zz-def)
(my/create-defun 'zz-ghi)

现在,让我们尝试解决此问题:

;; Rewriting the original macro as a function and using a
;; macro to collect the generated forms gives:
(defun my/create-defun (defun-name)
  `(defun ,defun-name ()
     (interactive)
     (let ((fn-name (symbol-name ',defun-name)))
       (message "Testing creation of function %s" fn-name))))

(defmacro my/create-defuns (defuns)
  `(progn ,@(mapcar 'my/create-defun defuns)))

(macroexpand '(my/create-defuns (zz-abc zz-def zz-ghi)))
;; Just to make sure
(progn
  (defun zz-abc nil
    (interactive)
    (let ((fn-name (symbol-name (quote zz-abc))))
      (message "Testing creation of function %s" fn-name)))
  (defun zz-def nil
    (interactive)
    (let ((fn-name (symbol-name (quote zz-def))))
      (message "Testing creation of function %s" fn-name)))
  (defun zz-ghi nil
    (interactive)
    (let ((fn-name (symbol-name (quote zz-ghi))))
      (message "Testing creation of function %s" fn-name))))

从变量读取函数名称的示例

(defvar my/functions '((func-1 . 1) (func-2 . 2) (func-3 . 3)))

(defun my/create-defun-n (defun-name n)
  `(defun ,defun-name ()
     (message "function: %s, n %d" ',defun-name ,n)))

(defmacro my/create-defuns-var ()
  `(progn ,@(mapcar
             (lambda (x) (my/create-defun-n (car x) (cdr x)))
             my/functions)))

(macroexpand '(my/create-defuns-var))
(progn
  (defun func-1 nil (message "function: %s, n %d" (quote func-1) 1))
  (defun func-2 nil (message "function: %s, n %d" (quote func-2) 2))
  (defun func-3 nil (message "function: %s, n %d" (quote func-3) 3)))

问题是概念上的:宏用于在环境想要读取代码时生成代码。当您自己(作为程序的用户)执行代码时,这样做已经太迟了(环境应该届时才能知道程序是什么)。


注意:我建议不要将几个混在一起defuns。原因是它使调试更加复杂。您在重复定义中拥有的很少的冗余在维护阶段将获得非常好的回报(并且维护通常是程序生命周期中最长的阶段)。


4
我想最后旁注应该在所有大胆的帽子:)
ABO血型ABO

谢谢!举个例子,这是很棒的信息。一旦确定mapcar与清单一起使用,我将接受此作为答案。这似乎不适用于我的实际用例。我会尽快研究这个问题。
Kaushal Modi

@kaushalmodi,您可以(mapcar (lambda (x) (message "argument: %s" x)) some-alist)查看得到的参数是什么,然后从那里开始工作。如果这是一个关联列表,我可以想象输出是类似的东西argument: (foo . bar),那么您可以foo使用carbar使用cdr函数进行访问。
wvxvw 2015年

是的,我做了同样的事情(只是我使用了nthfn而不是carand cadr),但是sequencep签入mapcar错误了。我提供了一个清单作为输入,但是mapcar仍然不认为那是一个序列。如果我这样做了(sequencep my-alist),那不是零。所以我很困惑..我还得调试一下。
Kaushal Modi

@kaushalmodi我可以想象两个原因:my-alistwas nil或您忘记了(或添加了额外的)引号,所以它my-alist要么是一个符号,要么被进一步评估为其他名称。您可能希望使用新代码扩展问题,使其更易于回答。
wvxvw 2015年

2
(dolist (fun '(foo bar baz))
  (defalias fun (lambda (a)
                  "I'm a function defined in `dolist'!"
                  (interactive)
                  (message a))))
(bar "See? No macro!")

并非完全无效,但为什么不呢?:P


0

我的初始化文件中包含以下内容:

(my/work-properties '("hostname" "username" "location"))

(defmacro jlp/make-def (name props doc &rest body)
  "Shortcut to programatically create new functions"
  (let ((funsymbol (intern name)))
    `(defun ,funsymbol ,props ,doc ,@body)))

(defun my/make-work-props (properties)
  "Create functions to retrieve properties from Org headlines."
  (dolist (prop properties)
    (let ((funsym   (format "my/org-get-%s" prop))
          (property (intern (format ":%s" (upcase prop))))
          (doc      (format "Retrieves `%s' from current headline"
                            (upcase prop)))
          (err (format "%s is not set" (capitalize prop))))
      (eval
       `(jlp/make-def ,funsym
                      ()
                      ,doc
                      (interactive)
                      (let ((x (or
                                (save-excursion
                                  (org-back-to-heading)
                                  (org-element-property
                                   ,property
                                   (org-element-at-point)))
                                (user-error ,err))))
                        (message "%s" x)
                         (kill-new x)))))))

(my/make-work-props my/org-work-properties)

它可能比需要的稍微复杂(尤其是额外的评估),但是它确实允许我生成这些属性所需的defun(在字符串中包含具有正确信息的docstring)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.