Özel ortamın sözcük ortamından alınmasına izin ver

Kodumu sözcüksel bağlama kullanmaya çevirmeye çalışıyorum.

Kullanıcı-özelleştirilebilir bir listeden bir lambda işlevini ( replaceer-iç 'e bağlı) çağıran bir işlevim ( format-template ) var ( template-replace Bir eşleme dizgisine dayalı işlevlerin işlevleri ). Değiştirici iç işlevi hiçbir argüman almaz ve bir dizge döndürür, ancak foo bar ve/veya değişkenlerini kullanması gerekebilir olabilir listesinin argümanının bir parçası olarak format-template 'e aktarılan baz .

(setq lexical-binding t)

(defcustom template-replace-functions
  '(("email"
     (lambda() user-mail-address)
    ("apples"
     (lambda ()
       (cond ((= foo 1)
              "one")
             ((= foo 2)
              "two")
             ((= foo 3)
              "three")
             (t "four"))))
    ("bananas"
     (lambda ()
       (if (eq bar 'move)
           "movement" "sit still")))))
  "Association list of replacement functions.

For each STRING, the corresponding FUNCTION is called with no
arguments and must return a string."
  :type '(repeat (group string function))
  :group 'spice-girls)

(defun format-template (list)
  (let* ((foo (nth 0 list))
         (bar (nth 1 list))
         (baz (nth 2 list))
         template
         (replacer-outer
          (lambda ()
            (replace-regexp-in-string
             "\${\([^\s\t\n]*?\)}"
             (lambda (match)
               (let* ((key (match-string 1 match))
                      (replacer-inner
                       (cadr (assoc key template-replace-functions))))
                 (if (and replacer-inner
                          (stringp (funcall replacer-inner)))
                     (funcall replacer-inner) "")))
             template t t))))
    (setq template
          ...)
    (if template (funcall replacer-outer))))

Örneğin, list , foo bar ve baz 'a bağlı üç öğeyi içerir. Eşleme dizesinin tuşu "bananas" olup template-replace-functions öğesinde bir işleve bağlanır ve replace- değerini ayarlar. iç için:

(lambda ()
       (if (eq bar 'move)
           "movement" "sit still"))

Bu noktada, yukarıdaki lambda işlevinin, dinamik bağlamayı kullanarak, ancak sözcüksel bağlamayı kullanarak çok iyi olmayan bar 'ı bilmesi gerekir.

Sorum şu; yerine replace-iç olan lambda işlevinin foo bar değerine bağlı olmasını sağlamak için bunu nasıl yaparım? ve baz ?

(Sadece bir şeyi karıştırmak için replace-internal içeren replace-outer fonksiyonu, gerçek fonksiyondaki iki yerden birinden çağrıldığı için izinlidir. Buraya satır içi yazabilirdim, ancak problemi eklediğinde bu şekilde dahil ettim.)

Düzenle:

Yarı yolda ...

(setq lexical-binding t
  foo 0
  bar 0)

(setq inline-fun-1
      '(lambda ()
         (setq return
               (if (eq foo 1)
                   "Pass" "Fail"))))

(defmacro lex-fun ()
  `(let* ((foo 1)
          (bar 1)
          return)
     (funcall ,inline-fun-1)))

(lex-fun)    ; -> "Pass"

(defun inline-fun-2 ()
  (setq return
        (if (eq bar 1)
            "Pass" "Fail")))

(defmacro lex-fun ()
  `(let* ((foo 1)
          (bar 1)
          return)
     (funcall ,inline-fun-2)))

(lex-fun)    ; -> Lisp error: (void-variable inline-fun-2)

Yani, bir makro içindeki lambda işlevini genişletmek işe yarayabilir, ancak adlandırılmış bir işlev değil. Ama kullanıcı lambda veya adlandırılmış işlevlere izin vermek istiyorum. Bu konuda nasıl geçilir?

3
@rnkn Bu, Emacs için bir işlev değil, sadece birkaç durumda örtük olarak dönüştürülecek bir liste. Fakat kayda değer farklılıklar vardır: Lambda listeleri çevreleyen bağları kapatamaz ve asla byte derlenmez. Onlar sadece tarihsel nedenlerle var olurlar, çünkü onlar iyi bir fikirdirler. Bugünün Emacs Lisp kodunda bu özelliğe güvenmemelisiniz.
katma yazar Ishmaeel, kaynak
Eğer tercüman (Emacs) onu bir işlev olarak görür ve kullanıcı bir işlev olarak görürse, pragmatik olarak konuşursak, bu bir işlev değil midir? Emacs, onu bir işlev olarak görecek kadar güzel olmaz mı?
katma yazar Iker Jimenez, kaynak
BTW, resminizi (lambda() kullanıcı-posta adresiniz) olarak adlandırmak bir işlev değildir. Bunun yerine, bir işlev için kaynak koduyla aynı görünmesi gereken 3 öğenin bir listesidir. Ve bu listeyi funcall 'a aktarırsanız, Emacs birini diğerine çevirecek kadar güzel. Ama gerçekten bunun bir işlev olduğunu söylemek istiyorsanız, o zaman `((" e-posta ", (lambda() kullanıcı-posta-adresi) yazmanız gerekir ...
katma yazar sds, kaynak

1 cevap

Kısa cevap: Yapamazsın. Bu tür bir bağlamanın "sözcüksel" olarak adlandırılmasının nedeni budur - değişkenlerin tanımlama alanından kaçmasını engeller.

Bu soruna hitap etmenin bir yolu: "muzlar" işlevine bazı veri yapısını geçirebildiniz, burada "muzlar" dan değerlerin nasıl çıkarılacağını bilirdi. Yani

(defcustom template-replace-functions
  '(("bananas" . (lambda (env) 
                   (let ((x (gethash "x" env "")))
                     (do-replacements-with x))))
    ...)))

Hangi env 'un bir hash-tablosu olduğunu varsayar.

Bir kenara göre: eq yerine eql kullanmak daha iyidir. Emacs Lisp'in, eq 'in hemen hemen her tür veri ile nasıl davrandığına dair herhangi bir yararlı garanti vermediğini sanmıyorum (örneğin, aynı isimde bulunan dahili sembollerin bile eşit olması gerektiğinden emin değilim < kod> eşdeğer ).

1
katma
Eq ile ilgili son söz için aşağı oy verdim. Sayısal olanların dışındaki tüm veri türleri için denklemin tam olarak eşdeğer olduğunun farkında mısınız? Ayrıca, aynı ismi paylaşsalar bile, iki sonsuz semboller aynı değildir ve bu nedenle denklemin altında eşit olmamalıdır.
katma yazar Ishmaeel, kaynak
Tüm saygımla Emacs Lisp, Ortak Lisp değil, ne de olacak. Emacs Lisp'in C gibi daha fazla alması durumunda çıkarılabileceği için listelerden kaçınabilirsiniz. Lütfen cevabınızdan son paragrafı kaldırın; kafa karıştırıcı ve yanıltıcıdır. Bu arada dahili semboller, Emacs Lisp'de sadece tek bir obarray olduğu için, denklemin altında eşittir.
katma yazar Ishmaeel, kaynak
@wvxvw Bu nasıl bir argüman? Ayrıca Haskell'in tip sisteminin de arkasında bir neden var, ama Emacs Lisp de Haskell olmayacak. Emacs Lisp, Ortak Lisp değildir, lütfen bunu aşın ve Common Lisp'in nasıl davrandığına dayanarak Emacs Lisp tavsiyesi vermeyin.
katma yazar Ishmaeel, kaynak
@wvxvw Peki, her neyse. Emacs Lisp, bugünlerde CL'den biraz farklıdır (C ++, tarihsel ilişkiye rağmen C ++ gibi), ve eq ile ilgili noktanız basitçe eski için geçerli değildir, ama sanırım bunu tartışmak için bir nokta yok. Ancak CL ile ilgili cevaplar oyladıysa, o zaman şaşırmayın.
katma yazar Ishmaeel, kaynak
@lunaryorn, yazdıklarımı okursanız, "Aynı isimde" interned sembolleri bile eq altında olmalıdır emin değilim "diyor. <�İ> Uninterned sembolleri hakkında hiçbir şey söylemedim. eq kullanma konusundaki gönülsüzlüğüm, Common Lisp'e bağlıdır çünkü burada bu neredeyse hiçbir zaman iyi bir şey değildir. Emacs Lisp'in uygulama detaylarını bilmiyorum, bu yüzden benim tarafımda bir batıl inanç olabilir. Yine de bundan kaçınılıyorum: Asla bilemezsiniz, belki de gelecek Emacs Lisp daha çok Common Lisp gibi olacak?
katma yazar Yann Trevin, kaynak
@lunaryorn Common Lisp keyfi yapmadı. Bunun bir nedeni var. Bu bir hız/bellek optimizasyonu. Kim bilir, belki bir gün Emacs Lisp hafızasının nasıl yönetildiğine daha fazla optimizasyon yapmaya çalışacaktır, o zaman sorun olabilir.
katma yazar Yann Trevin, kaynak
@rnkn Peki, bu sadece çocukça. Elbette, siz 'in ne yapabileceği için bir kısıtlama değildir. Kesinlikle yapabilirsin. İstediğiniz şekilde çalışıp çalışmadığı farklı bir konudur.
katma yazar Yann Trevin, kaynak
@lunaryorn Emacs Lisp, tarihsel olarak Common Lisp'in çok yakın bir akrabası olmuştur. Pek çok benzer kaygısı var ve Haskell'den çok farklı. Ancak, bu sadece Common Lisp'in bir davranışı değildir. Bu, herhangi bir çalışma zamanının tasarımcısının bir noktada buluşması gereken bir şeydir. Bu aynı zamanda, Java'daki dizeleri özel işlev kullanarak karşılaştırmanızın nedenidir, neden işaretçileri C'deki harf dizileriyle karşılaştırmak iyi bir fikir değildir vb. Söylediğim şey, şeylerin tarif ettiğim gibi olmasının bir sebebi olduğudur. ve şimdi değillerse, gelecekte olabilirler.
katma yazar Yann Trevin, kaynak
@lunaryorn Daha az önemsemedim.
katma yazar Yann Trevin, kaynak
Yapabilirim ve yapacağım! Soruyu bir makro kullanarak kısmi bir çözüm içerecek şekilde düzenledim, ancak yine de tam bir çözüm değil.
katma yazar Iker Jimenez, kaynak
env komutunun özel işlevlere geçirilmesi ile ilgili sorun, kullanıcının foo bar ve baz ve bunun yeterince kullanıcı dostu olmadığını hissediyorum. Ancak bu, bilinmeyen bir sözcük ortamını kullanıcıya yeniden gözden geçirmemi sağladı. Kötü olabilir.
katma yazar Iker Jimenez, kaynak