Bagaimana saya bisa membuat banyak pembelaan dengan mengulang daftar?

11

Saya sedang berupaya mengoptimalkan konfigurasi emacs saya di mana saya dapat secara dinamis membuat fungsi interaktif untuk semua tema yang saya miliki dalam daftar.

Di bawah ini adalah versi sederhana dari konstruk yang saya coba buat.

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

Tetapi jika saya membuka gulungan secara manual, itu bekerja:

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

Tetapi di bawah ini tidak berfungsi di mana saya memberikan nama simbol (yang mungkin adalah apa yang terjadi ketika loop terbuka dengan sendirinya). Catat kutipan sebelum argumen makro.

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

Memperbarui

Terima kasih atas bantuan @wvxvw , akhirnya saya berhasil !

Seperti yang disarankan @wvxvw, saya tidak akan menjadi penggila batch untuk setiap kasus penggunaan. Ini adalah kasus penggunaan khusus di mana untuk tema bernama XYZ, saya ingin menghasilkan defun disebut load-theme/XYZyang melakukan pekerjaan

  • Menonaktifkan semua tema lain yang mungkin aktif
  • Memanggil load-themeuntukXYZ
  • Melakukan beberapa hal khusus yang terkait dengan tema itu; Saya melewati pengaturan khusus untuk setiap tema melalui daftar my/themesalist.
Kaushal Modi
sumber
1
Masukkan semua ke defunsdalam a progn. progndiizinkan untuk menjadi formulir tingkat atas (dalam arti bahwa segala sesuatu yang berlaku untuk formulir tingkat atas berlaku untuk konten prognjuga). Tetapi saya akan mempertanyakan alasan menciptakan fungsi sedemikian rupa: mengapa tidak, katakanlah, memiliki tabel dengan lambdas sebagai nilai?
wvxvw
@ wvxvw Saya tidak mengerti saran itu. Saya baru saja membuat defun makro yang ingin saya panggil beberapa kali dalam satu lingkaran. Contoh yang belum dibuka secara manual adalah untuk menunjukkan apa yang berhasil dan tidak berfungsi saat saya mencoba mencari tahu masalah ini. Tujuan saya adalah memiliki daftar alih-alih daftar dan membuat fungsi interaktif untuk berbagai tema . Saat ini daftar hanya terdiri dari conses tetapi saya berencana untuk mengonversinya menjadi daftar dengan properti khusus untuk setiap tema.
Kaushal Modi
Nah, Anda menelepon (my/create-defun name)3 kali, jadi Anda harus mendefinisikan fungsi yang disebut name3 kali.
Omar

Jawaban:

13

Berikut adalah upaya menjelaskan dan beberapa saran.

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

Sekarang, mari kita coba perbaiki ini:

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

Contoh dengan membaca nama fungsi dari variabel

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

Masalahnya adalah dari jenis konseptual: makro adalah untuk menghasilkan kode ketika lingkungan ingin membacanya. Ketika Anda mengeksekusi kode sendiri (sebagai pengguna program Anda) ini sudah terlambat untuk melakukannya (lingkungan harus tahu pada saat itu apa program itu).


Sebuah catatan pinggir: Saya akan menyarankan agar tidak menyatukan beberapa defuns. Alasannya adalah itu membuat debuggin jauh lebih rumit. Sedikit redundansi yang Anda miliki dalam definisi berulang terbayar dengan sangat baik selama fase pemeliharaan (dan pemeliharaan biasanya merupakan fase terpanjang dalam masa hidup program).

wvxvw
sumber
4
Saya pikir catatan marjinal terakhir harus dalam huruf tebal semua :)
abo-abo
Terima kasih! Itu informasi yang bagus dengan contoh. Saya akan menerima ini sebagai jawaban segera setelah saya mencari tahu mapcardengan alists. Ini sepertinya tidak bekerja dengan use case saya yang sebenarnya. Saya akan menggali ini secepat mungkin.
Kaushal Modi
@kaushalmodi Anda dapat menempatkan (mapcar (lambda (x) (message "argument: %s" x)) some-alist)apa argumen yang Anda dapatkan, dan bekerja dari sana. Jika itu daftar asosiatif, saya bayangkan outputnya seperti argument: (foo . bar), maka Anda dapat mengakses foomenggunakan cardan barmenggunakan cdrfungsi.
wvxvw
Ya, saya melakukan hal yang sama (hanya saja saya menggunakan nthfn alih-alih cardan cadr) tetapi sequencepcheck-in mapcarsalah. Saya memberikan alist sebagai input tetapi mapcar masih tidak berpikir itu adalah urutan. Jika saya melakukannya (sequencep my-alist), itu bukan nol. Jadi saya bingung .. Saya harus men-debug itu.
Kaushal Modi
@kaushalmodi Saya akan membayangkan dua alasan: my-alistadalah nilatau Anda lupa (atau menambahkan ekstra) tanda kutip sehingga my-alistbisa menjadi simbol, atau dievaluasi lebih jauh untuk menjadi sesuatu yang lain. Anda mungkin ingin memperluas pertanyaan Anda dengan kode baru agar lebih mudah dijawab.
wvxvw
2
(dolist (fun '(foo bar baz))
  (defalias fun (lambda (a)
                  "I'm a function defined in `dolist'!"
                  (interactive)
                  (message a))))
(bar "See? No macro!")

Tidak benar-benar defun tapi mengapa tidak? : P

JAre
sumber
0

Saya memiliki yang berikut di init saya:

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

Ini mungkin sedikit lebih kompleks daripada yang dibutuhkan (terutama eval tambahan) tetapi itu memungkinkan saya untuk menghasilkan defuns yang saya butuhkan untuk properti-properti tersebut (dan memasukkan dokumen dengan informasi yang benar dalam string).

Jonathan Leech-Pepin
sumber