Bir listede dolaşarak nasıl birden fazla hata oluşturabilirim?


11

Üzerinde çalışıyorum konfigürasyon dosyasında benim emacs optimize Dinamik bir listede olan tüm temalar için etkileşimli işlevler oluşturabilirsiniz nerede.

Aşağıda iş yapmaya çalıştığım yapının basitleştirilmiş bir versiyonu var.

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

Ancak döngüyü elle açarsam çalışır:

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

Ama aşağıda sembol isimlerini geçtiğim yerde çalışmıyor (muhtemelen döngü kendi kendine açıldığında olan şey). Makro bağımsız değişkenlerinden önce tırnak işaretleri not alın.

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

Güncelleme

Teşekkür etmek @wvxvw s' yardım, ben nihayet bu çalışma var !

@Wvxvw'nin da belirttiği gibi, her kullanım için toplu üretme kusurları olmayacağım. Bu, adlı bir tema için işini yapan XYZadlı bir defun oluşturmak istediğim özel bir kullanım durumuyduload-theme/XYZ

  • Etkin olabilecek diğer tüm temaları devre dışı bırakma
  • Arama load-themeiçinXYZ
  • Bu temayla ilgili daha özel şeyler yapmak; Her tema için özel ayarları my/themesalist üzerinden iletiyorum.

1
Hepsini defunsiçine koyun progn. prognüst düzey bir form olmasına izin verilir (üst düzey formlar için geçerli olan her şeyin de içeriği için geçerli olduğu anlamına gelir progn). Ama bu şekilde fonksiyon yaratmanın mantığını sorgulayacağım: neden değerler olarak lambdas içeren bir tablo yok?
wvxvw

@wvxvw Öneriyi anlamadım. Ben bir döngüde birden çok kez aramak istiyorum makro oluşturma sadece bir defun var. Elle açılmış örnekler, bu sorunu anlamaya çalışırken neyin işe yarayıp neyin işe yaramadığını göstermektir. Amacım bir liste yerine bir listeye sahip olmak ve çeşitli temalar için etkileşimli işlevler oluşturmaktır . Şu anda alist sadece conses oluşur ama ben her tema için özel özelliklere sahip listelere dönüştürmek planlıyoruz.
Kaushal Modi

Peki, (my/create-defun name)3 kez aradın , bu yüzden name3 kez denilen bir işlevi tanımlamak gerekir .
Omar

Yanıtlar:


13

İşte açıklamaya yönelik bir girişim ve bazı öneriler.

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

Şimdi bunu düzeltmeye çalışalım:

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

Değişkenden işlev adlarını okuyan örnek

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

Sorun kavramsal bir türdeydi: makrolar çevre okumak istediğinde kod üretmek içindir. Kodu kendiniz yürüttüğünüzde (programınızın kullanıcısı olarak) bu zaten bunu yapmak için çok geç (ortam o zaman programın ne olduğunu bilmelidir).


Marjinal bir not: Birkaç kişinin birlikte toplanmasını önermeliyim defuns. Bunun nedeni, hata ayıklamayı çok daha karmaşık hale getirmesidir. Tekrarlanan tanımlarda sahip olduğunuz az fazlalık, bakım aşamasında çok iyi sonuç verir (ve bakım genellikle programın ömrünün en uzun aşamasıdır).


4
Sanırım son marjinal not tüm kalın harflerle yazılmalıdır :)
abo-abo

Teşekkürler! Örnek olarak harika bir bilgi. mapcarAlistlerle birlikte kullandığım andan itibaren bunu bir cevap olarak kabul edeceğim . Bu benim gerçek kullanım durumumla çalışmıyor gibi görünüyor. Bunu olabildiğince çabuk kazacağım.
Kaushal Modi

@kaushalmodi (mapcar (lambda (x) (message "argument: %s" x)) some-alist), aldığınız argümanın ne olduğunu görüp oradan çalışabilirsiniz. Bu ilişkisel bir liste ise, çıktıya benzer bir şey olduğunu hayal ederdim argument: (foo . bar), o zaman işlevleri fookullanarak carve barkullanarak erişebilirsiniz cdr.
wvxvw

Evet, aynı şeyi yaptım (sadece nthfn yerine carve kullandım cadr) ama sequencepcheck in mapcarhatalı. Girdi olarak bir alist veriyordum ama yine de mapcar bunun bir sıra olduğunu düşünmüyordu. Eğer yapsaydım (sequencep my-alist), bu sıfır değildi. Bu yüzden kafam karıştı .. Henüz hata ayıklamam gerekiyor.
Kaushal Modi

: @kaushalmodi İki nedenlerini hayal ediyorum my-alistoldu nilya o yüzden tırnak unuttum (veya ekstra eklendi) my-alistya bir semboldü, ya da başka bir şey olmak daha da değerlendirilmiştir. Yanıtlamayı kolaylaştırmak için muhtemelen sorunuzu yeni kodla genişletmek istersiniz.
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!")

Tam defuns değil ama neden olmasın? : P


0

Başlamamda aşağıdakiler var:

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

Belki de gerekenden biraz daha karmaşıktır (özellikle ekstra değerlendirme), ancak bu özellikler için ihtiyacım olan kusurları üretmeme izin veriyor (ve dizelerde doğru bilgilere sahip docstrings'i dahil etmeme).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.