Làm thế nào tôi có thể tạo nhiều defun bằng cách lặp qua một danh sách?


11

Tôi đang làm việc để tối ưu hóa cấu hình emacs của mình , nơi tôi có thể tự động tạo các chức năng tương tác cho tất cả các chủ đề tôi có trong danh sách.

Dưới đây là phiên bản đơn giản hóa của cấu trúc tôi đang cố gắng thực hiện.

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

Nhưng nếu tôi hủy đăng ký vòng lặp theo cách thủ công, nó sẽ hoạt động:

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

Nhưng bên dưới không hoạt động khi tôi chuyển vào các tên biểu tượng (có thể là những gì đang xảy ra khi vòng lặp tự kiểm soát). Lưu ý các trích dẫn trước các đối số vĩ mô.

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

Cập nhật

Nhờ sự giúp đỡ của @wvxvw , cuối cùng tôi cũng đã làm việc này !

Như @wvxvw gợi ý, tôi sẽ không tạo ra các đợt giảm giá hàng loạt cho bất kỳ và mọi trường hợp sử dụng. Đây là trường hợp sử dụng đặc biệt trong đó đối với một chủ đề có tên XYZ, tôi muốn tạo một defun được gọi là load-theme/XYZcông việc của

  • Vô hiệu hóa tất cả các chủ đề khác có thể hoạt động
  • Gọi load-themechoXYZ
  • Làm một số công cụ tùy chỉnh hơn liên quan đến chủ đề đó; Tôi chuyển qua các cài đặt tùy chỉnh cho từng chủ đề thông qua my/themesalist.

1
Cho tất cả defunsvào trong a progn. prognđược phép là một hình thức cấp cao nhất (theo nghĩa là mọi thứ áp dụng cho các hình thức cấp cao nhất đều áp dụng cho nội dung của prognquá). Nhưng tôi sẽ đặt câu hỏi về lý do của việc tạo các hàm theo cách như vậy: tại sao không có, ví dụ, có một bảng có lambdas làm giá trị?
wvxvw 18/03/2015

@wvxvw Tôi không hiểu đề xuất. Tôi chỉ có một defun tạo macro mà tôi muốn gọi nhiều lần trong một vòng lặp. Các ví dụ không được kiểm soát thủ công là để hiển thị những gì đã hoạt động và không hoạt động trong khi tôi đang cố gắng tìm ra vấn đề này. Mục đích của tôi là có một danh sách thay vì một danh sách và tạo các chức năng tương tác cho các chủ đề khác nhau . Hiện tại alist chỉ bao gồm conses nhưng tôi dự định chuyển đổi chúng thành danh sách với các thuộc tính tùy chỉnh cho từng chủ đề.
Kaushal Modi

Chà, bạn đã gọi (my/create-defun name)3 lần, vì vậy bạn nên kết thúc việc xác định hàm gọi là name3 lần.
Omar

Câu trả lời:


13

Đây là một nỗ lực giải thích và một số gợi ý.

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

Bây giờ, chúng ta hãy cố gắng khắc phục điều này:

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

Ví dụ với việc đọc tên hàm từ một biến

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

Vấn đề thuộc loại khái niệm: macro là để tạo mã khi môi trường muốn đọc nó. Khi bạn tự thực thi mã (với tư cách là người dùng chương trình của mình) thì điều này đã quá muộn để thực hiện (môi trường nên biết sau đó chương trình là gì).


Một lưu ý ngoài lề: Tôi sẽ khuyên bạn không nên kết hợp với nhau defuns. Lý do là nó làm cho việc gỡ lỗi phức tạp hơn rất nhiều. Sự dư thừa nhỏ mà bạn có trong các định nghĩa lặp đi lặp lại rất tốt trong giai đoạn bảo trì (và bảo trì thường là giai đoạn dài nhất trong thời gian sống của chương trình).


4
Tôi nghĩ rằng ghi chú bên lề cuối cùng phải có trong tất cả các chữ in đậm :)
abo-abo

Cảm ơn! Đó là thông tin tuyệt vời với ví dụ. Tôi sẽ chấp nhận điều này như một câu trả lời ngay khi tôi tìm ra cách sử dụng mapcarvới alists. Điều này dường như không hoạt động với trường hợp sử dụng thực tế của tôi. Tôi sẽ đào sâu vào vấn đề này sớm nhất có thể.
Kaushal Modi

@kaushalmodi bạn có thể đặt (mapcar (lambda (x) (message "argument: %s" x)) some-alist)để xem đối số bạn nhận được là gì và làm việc từ đó. Nếu đó là một danh sách liên kết, tôi sẽ tưởng tượng đầu ra giống như thế argument: (foo . bar), thì bạn có thể truy cập foobằng cách sử dụng carbarsử dụng các cdrchức năng.
wvxvw 19/03/2015

Có, tôi đã làm như vậy (chỉ là tôi đã sử dụng nthfn thay vì carcadr) nhưng sequencepkiểm tra sai mapcar. Tôi đã cung cấp một alist làm đầu vào nhưng mapcar vẫn không nghĩ đó là một chuỗi. Nếu tôi đã làm (sequencep my-alist), đó là không. Vì vậy, tôi bối rối .. Tôi phải gỡ lỗi đó.
Kaushal Modi

@kaushalmodi tôi sẽ tưởng tượng hai lý do: my-alistđã nilhoặc bạn quên (hoặc bổ sung thêm) dấu ngoặc kép để my-alisthoặc là một biểu tượng, hoặc được đánh giá hơn nữa để được cái gì khác. Bạn có thể muốn mở rộng câu hỏi của mình với mã mới để dễ trả lời hơn.
wvxvw 19/03/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!")

Không chính xác defun nhưng tại sao không? : P


0

Tôi có những điều sau đây trong init của mình:

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

Nó có thể hơi phức tạp hơn mức cần thiết (đặc biệt là thêm eval) nhưng nó cho phép tôi tạo ra các defun tôi cần cho các thuộc tính đó (và bao gồm các tài liệu với thông tin chính xác trong chuỗi).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.