リストをループして複数の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、私はと呼ばれる関数定義を生成したいload-theme/XYZの仕事をしていません

  • アクティブになる可能性のある他のすべてのテーマを無効にする
  • 呼び出しload-themeのためにXYZ
  • そのテーマに関連するいくつかのカスタム設定を行います。各テーマのカスタム設定をmy/themesリストから渡します。

1
すべてdefunsprognprognトップレベルのフォームであることが許可されます(トップレベルのフォームに適用されるすべてがその内容にprognも適用されるという意味で)。しかし、私はそのような方法で関数を作成する理論的根拠に疑問を投げかけます:値としてラムダを持つhas-tableを持たないのはなぜですか?
wvxvw 2015年

@wvxvw提案が理解できませんでした。ループ内で複数回呼び出したいdefun作成マクロが1つだけあります。手動で展開した例は、私がこの問題を解決しようとしたときに何が機能し、何が機能しなかったかを示しています。私の目的は、リストの代わりにalistを用意し、さまざまなテーマのインタラクティブな機能を作成することです。現在、alistはconses のみで構成されていますが、各テーマのカスタムプロパティを含むリストに変換する予定です。
Kaushal Modi

さて、(my/create-defun name)3回呼び出したので、3回という関数を定義する必要がありますname
Omar

回答:


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 2015年

ありがとう!これは、例を示す優れた情報です。mapcaralistsでの使用を理解したらすぐに、これを回答として受け入れます。これは私の実際の使用例ではうまくいかないようです。できるだけ早くこれを掘り下げます。
Kaushal Modi、2015

@kaushalmodiあなたは(mapcar (lambda (x) (message "argument: %s" x)) some-alist)あなたが得る議論が何であるかを見るために置くことができ、そこから作業することができます。それが連想リストである場合、出力はのようなものになると思います。そうすれば、関数を使用してargument: (foo . bar)アクセスできます。foocarbarcdr
wvxvw 2015年

はい、私は同じことをしましたが(andのnth代わりにfn を使用しただけです)、チェックインにエラーが発生しました。私は入力としてalistを提供していましたが、それでもmapcarはそれがシーケンスであるとは思っていませんでした。私がした場合、それはnilではありませんでした。だから私は混乱しています...私はまだそれをデバッグする必要があります。carcadrsequencepmapcar(sequencep my-alist)
Kaushal Modi 2015

@kaushalmodi私は2つの理由を想像:my-alistだった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!")

正確にはdefunsではありませんが、なぜそうではないのですか?:P


0

私は私のinitに以下を持っています:

(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)

それはおそらく必要以上に少し複雑です(特にその余分なeval)が、それらのプロパティに必要なdefunを生成することができます(そして文字列に正しい情報を含むdocstringを含めることができます)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.