ฉันจะสร้างหลาย defuns ได้โดยการวนซ้ำผ่านรายการได้อย่างไร


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)

แต่ด้านล่างไม่ได้ทำงานที่ฉันผ่านในชื่อสัญลักษณ์ (ซึ่งอาจเป็นสิ่งที่เกิดขึ้นเมื่อวนรอบ unrolls ด้วยตัวเอง) จดเครื่องหมายคำพูดก่อนอาร์กิวเมนต์ของแมโคร

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

ปรับปรุง

ขอบคุณ@wvxvw ในที่สุดฉันก็ได้งานนี้มา !

ตามที่ @wvxvw แนะนำฉันจะไม่ถูกสร้างเป็นชุดสำหรับกรณีการใช้งานทุกกรณี นี่เป็นกรณีการใช้งานพิเศษที่มีชื่อธีมXYZฉันต้องการสร้าง defun ที่เรียกload-theme/XYZว่าทำงาน

  • ปิดการใช้งานชุดรูปแบบอื่น ๆ ทั้งหมดที่อาจใช้งานอยู่
  • โทรload-themeหาXYZ
  • ทำสิ่งที่กำหนดเองเพิ่มเติมที่เกี่ยวข้องกับชุดรูปแบบนั้น ฉันผ่านการตั้งค่าที่กำหนดเองสำหรับแต่ละธีมผ่านอmy/themesลิส

1
ใส่ทั้งหมดภายในdefuns ได้รับอนุญาตให้เป็นแบบฟอร์มระดับบนสุด (ในแง่ที่ว่าทุกสิ่งที่ใช้กับแบบฟอร์มระดับบนสุดจะใช้กับเนื้อหาของด้วย) แต่ฉันจะถามเหตุผลของการสร้างฟังก์ชั่นในลักษณะดังกล่าว: ทำไมไม่มีพูดว่ามีตารางมีลูกแกะเป็นค่า? prognprognprogn
wvxvw

@wvxvw ฉันไม่เข้าใจคำแนะนำ ฉันมี defun เพียงอันเดียวที่สร้างมาโครซึ่งฉันต้องการโทรหลายครั้งในลูป ตัวอย่างที่ไม่ได้ควบคุมด้วยตนเองคือแสดงสิ่งที่ใช้งานได้และไม่ได้ผลในขณะที่ฉันพยายามหาปัญหานี้ จุดมุ่งหมายของฉันคือการมี alist แทนของรายการและสร้างฟังก์ชั่นแบบโต้ตอบสำหรับรูปแบบต่างๆ ขณะนี้ alist ประกอบด้วยconses เท่านั้นแต่ฉันวางแผนที่จะแปลงรายการเหล่านั้นด้วยรายการคุณสมบัติที่กำหนดเองสำหรับแต่ละชุดรูปแบบ
Kaushal Modi

ทีนี้, คุณเรียก(my/create-defun name)3 ครั้ง, ดังนั้นคุณควรไขฟังก์ชั่นที่เรียกว่าname3 ครั้ง
โอมา

คำตอบ:


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ฉันจะให้คำแนะนำกับก้อนด้วยกันหลาย เหตุผลก็คือมันทำให้ debuggin มีความซับซ้อนมากขึ้น ความซ้ำซ้อนเล็ก ๆ น้อย ๆ ที่คุณมีในคำจำกัดความซ้ำแล้วซ้ำอีกจะให้ผลตอบแทนที่ดีมากในระหว่างขั้นตอนการบำรุงรักษา


4
ผมคิดว่าโน้ตร่อแร่สุดท้ายควรจะอยู่ในหมวกหนาทั้งหมด :)
Abo-Abo

ขอบคุณ! นั่นเป็นข้อมูลที่ยอดเยี่ยมพร้อมตัวอย่าง ฉันจะยอมรับสิ่งนี้เป็นคำตอบทันทีที่ฉันเข้าใจการใช้mapcarกับ alists ดูเหมือนจะไม่ทำงานกับกรณีการใช้งานจริงของฉัน ฉันจะขุดมันให้เร็วที่สุด
Kaushal Modi

@kaushalmodi คุณสามารถใส่(mapcar (lambda (x) (message "argument: %s" x)) some-alist)เพื่อดูอาร์กิวเมนต์ที่คุณได้รับคืออะไรและทำงานจากที่นั่น หากเป็นรายการที่เชื่อมโยงกันฉันจะจินตนาการว่าผลลัพธ์เป็นสิ่งที่ต้องการargument: (foo . bar)แล้วคุณสามารถเข้าถึงการfooใช้carและการbarใช้cdrฟังก์ชั่นได้
wvxvw

ใช่ฉันทำแบบเดียวกัน (แค่ว่าฉันใช้nthfn แทนcarและcadr) แต่sequencepเช็คอินmapcarผิดพลาด ฉันให้อลิสต์เป็นอินพุท แต่ก็ยังไม่ได้คิดว่ามันเป็นลำดับคาร์คาร์ หากฉันทำ(sequencep my-alist)เช่นนั้น ดังนั้นฉันสับสน .. ฉันยังต้องแก้ไขข้อบกพร่องนั้น
Kaushal Modi

@kaushalmodi ฉันคิดเหตุผลสองประการmy-alistคือnilหรือคุณลืม (หรือเพิ่มเสริม) คำพูดเพื่อให้my-alistเป็นทั้งสัญลักษณ์หรือถูกประเมินให้ดียิ่งขึ้นที่จะเป็นอย่างอื่น คุณอาจต้องการขยายคำถามของคุณด้วยรหัสใหม่เพื่อให้ง่ายต่อการตอบ
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!")

ไม่ได้กำหนดแน่นอน แต่ทำไมไม่ : 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 พิเศษ) แต่มันทำให้ฉันสามารถสร้าง defuns ที่ฉันต้องการสำหรับคุณสมบัติเหล่านั้นได้ (และรวมถึงเอกสารที่มีข้อมูลที่ถูกต้องในสตริง)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.