कस्टम फ़ंक्शन को लेक्सिकल वातावरण से लेने की अनुमति दें

मैं अपने कोड को लेक्सिकल बाइंडिंग का उपयोग करने के लिए कनवर्ट करने की कोशिश कर रहा हूं।

मेरे पास एक फ़ंक्शन ( format-template ) है जो उपयोगकर्ता-अनुकूलन सूची ( टेम्पलेट-प्रतिस्थापन से एक लैम्ब्डा फ़ंक्शन ( replacer-inner पर बाध्य) -फंक्शन ) मिलान-स्ट्रिंग के आधार पर फ़ंक्शंस का। प्रतिस्थापन-आंतरिक फ़ंक्शन कोई तर्क नहीं लेता है और एक स्ट्रिंग देता है, हालांकि, यह may चर का उपयोग करने की आवश्यकता है foo bar और/या baz , जो list को format-template पर तर्क के हिस्से के रूप में पारित किया गया है।

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

इसलिए, उदाहरण के लिए, list में तीन तत्व होते हैं, जो foo bar और baz पर बाध्य हैं। मिलान-स्ट्रिंग का कुंजी "केले" है जो template-replace-functions में किसी फ़ंक्शन से संबद्ध होता है और replace- आंतरिक से:

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

इस बिंदु पर, उपरोक्त लैम्ब्डा फ़ंक्शन को bar पता होना चाहिए, जो गतिशील बाध्यकारी का उपयोग करके ठीक है, लेकिन लेक्सिकल बाइंडिंग का उपयोग करके इतना अच्छा नहीं है।

मेरा सवाल यह है कि, मैं लैम्बडा फ़ंक्शन को replacer-inner से foo bar के लेट-बाध्य मानों से ले जाने के लिए कैसे कर सकता हूं। और baz ?

(बस चीजों को भ्रमित करने के लिए, replace-outer फ़ंक्शन, जिसमें replace-inner है, को छोड़ दिया गया है क्योंकि इसे वास्तविक फ़ंक्शन में दो स्थानों में से एक से कहा जाता है। मैं यहां इनलाइन लिखी थी लेकिन इस समस्या को जोड़ने के मामले में इसे शामिल किया गया था।)

संपादित करें:

आधे रास्ते वहाँ...

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

तो, एक मैक्रो के भीतर लैम्ब्डा फ़ंक्शन का विस्तार करना प्रतीत होता है, लेकिन नामित फ़ंक्शन नहीं। लेकिन मैं उपयोगकर्ता लैम्ब्डा या नामित कार्यों की अनुमति देना चाहता हूं। इस के आसपास कैसे हो?

3
@rnkn यह Emacs के लिए एक फ़ंक्शन नहीं है, यह केवल एक सूची है जो कुछ स्थितियों में अंतर्निहित रूप से परिवर्तित होती है। लेकिन उल्लेखनीय मतभेद हैं: लैम्ब्डा सूचियां आस-पास की बाइंडिंग पर बंद नहीं हो सकती हैं, और कभी भी बाइट संकलित नहीं होती हैं। वे ऐतिहासिक कारणों से पूरी तरह मौजूद हैं, न कि क्योंकि वे एक अच्छा विचार हैं। आपको आज के Emacs Lisp कोड में इस सुविधा पर भरोसा नहीं करना चाहिए।
जोड़ा लेखक Ishmaeel, स्रोत
@Stefan अगर दुभाषिया (Emacs) इसे एक समारोह के रूप में देखता है और उपयोगकर्ता इसे एक समारोह के रूप में देखता है, तो व्यावहारिक रूप से बोल रहा है, क्या यह एक समारोह नहीं है? क्या Emacs कभी भी एक समारोह के रूप में देखने के लिए पर्याप्त अच्छा नहीं होगा?
जोड़ा लेखक Iker Jimenez, स्रोत
बीटीडब्लू, औपचारिक रूप से आपके (lambda() उपयोगकर्ता-मेल-पता) बोल रहा है कोई फ़ंक्शन नहीं है। इसके बजाय, यह 3 तत्वों की एक सूची है, जो किसी फ़ंक्शन के स्रोत कोड के समान दिखाई देती है। और यदि आप उस सूची को funcall पर पास करते हैं तो Emacs एक दूसरे को चालू करने के लिए पर्याप्त है। लेकिन अगर आप वास्तव में यह कहना चाहते हैं कि यह एक कार्य है, तो आपको '(("ईमेल", (lambda() उपयोगकर्ता-मेल-पता लिखना होगा ...
जोड़ा लेखक sds, स्रोत

1 उत्तर

संक्षिप्त उत्तर: आप नहीं कर सकते। यही कारण है कि इस तरह के बाध्यकारी को "लेक्सिकल" कहा जाता है - यह चर परिभाषा को अपने परिभाषा क्षेत्र से बचने से रोकता है।

इस समस्या को हल करने का एक तरीका: आप "केले" फ़ंक्शन में कुछ डेटा-संरचना पारित कर सकते थे, जहां "केले" यह जान लेंगे कि इससे मूल्य कैसे निकालें। अर्थात।

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

जो मानता है कि env एक हैश-टेबल है।

एक तरफ के रूप में: eq के बजाय eql का उपयोग करना बेहतर है। मुझे नहीं लगता कि Emacs Lisp किसी भी उपयोगी प्रकार की जानकारी देता है कि कैसे eq लगभग किसी भी प्रकार के डेटा के साथ व्यवहार करता है (उदाहरण के लिए। मुझे यकीन नहीं है कि एक ही नाम के साथ भी आंतरिक प्रतीकों को बराबर < कोड> eq )।

1
जोड़ा
मैंने ईक के बारे में आखिरी टिप्पणी के लिए नीचे वोट डाला। क्या आप जानते हैं कि eq संख्यात्मक को छोड़कर सभी डेटा प्रकारों के बराबर बराबर है? इसके अलावा, दो अनियंत्रित प्रतीक समान नहीं हैं, भले ही वे एक ही नाम साझा करते हैं, और इसलिए ईक के बराबर नहीं होना चाहिए।
जोड़ा लेखक Ishmaeel, स्रोत
सभी उचित सम्मान के साथ, Emacs Lisp आम लिस्प नहीं है, और न ही कभी होगा। आप सूचियों से बच सकते हैं क्योंकि उन्हें हटाया जा सकता है जब Emacs Lisp सी की तरह अधिक हो जाता है। कृपया अपने उत्तर से अंतिम अनुच्छेद हटा दें; यह भ्रमित और भ्रामक है। रास्ते से आंतरिक प्रतीकों ईक के बराबर हैं, केवल इसलिए कि Emacs Lisp में केवल एक ही obarray है।
जोड़ा लेखक Ishmaeel, स्रोत
@wvxvw यह किस तरह का तर्क है? हास्केल की टाइप सिस्टम के पीछे भी एक कारण है, फिर भी, Emacs Lisp या तो हास्केल बनने वाला नहीं है। Emacs Lisp आम लिस्प नहीं है, इसलिए कृपया इसे प्राप्त करें और आम लिस्प व्यवहार के आधार पर Emacs Lisp सलाह न दें।
जोड़ा लेखक Ishmaeel, स्रोत
@wvxvw ठीक है, जो भी हो। Emacs Lisp आजकल सीएल से कुछ अलग है (जैसे ऐतिहासिक संबंधों के बावजूद सी ++ सी से है), और ईक के बारे में आपका बिंदु बस पूर्व पर लागू नहीं होता है, लेकिन मुझे लगता है कि इस पर आगे चर्चा करने में कोई बात नहीं है। लेकिन आश्चर्यचकित न हों अगर सीएल के बारे में जवाब तब मतदान हो जाएंगे।
जोड़ा लेखक Ishmaeel, स्रोत
@lunaryorn यदि आपने जो लिखा है उसे पढ़ते हैं तो यह कहता है, "मुझे यकीन नहीं है कि interned समान नाम वाले प्रतीकों को ईक के बराबर होना चाहिए"। मैंने अनियंत्रित प्रतीकों के बारे में कुछ भी नहीं कहा। eq का उपयोग करने की मेरी अनिच्छा सामान्य लिस्प के कारण है, जहां यह लगभग कभी भी अच्छी बात नहीं है। मैं Emacs Lisp के कार्यान्वयन विवरण नहीं जानता, इसलिए, यह मेरी तरफ सिर्फ एक अंधविश्वास हो सकता है। फिर भी मैं अभी भी इससे बचूंगा: आप कभी नहीं जानते, शायद भविष्य में Emacs Lisp Common Lisp की तरह अधिक होगा?
जोड़ा लेखक Yann Trevin, स्रोत
@lunaryorn आम लिस्प इसे मनमाने ढंग से नहीं करता है। इसका एक कारण है। यह एक गति/स्मृति अनुकूलन है। कौन जानता है, शायद एक दिन Emacs Lisp इसकी स्मृति को प्रबंधित करने के तरीके के बारे में अधिक अनुकूलन जोड़ने का प्रयास करेगा, फिर यह एक मुद्दा बन सकता है।
जोड़ा लेखक Yann Trevin, स्रोत
@rnkn ठीक है, यह सिर्फ बचपन है। बेशक यह आप क्या कर सकता है इसके लिए एक प्रतिबंध नहीं है। आप निश्चित रूप से ऐसा कर सकते हैं। चाहे वह काम करेगा चाहे आप इसे एक अलग मामला चाहते हैं।
जोड़ा लेखक Yann Trevin, स्रोत
@lunaryorn Emacs Lisp ऐतिहासिक लिस्प का ऐतिहासिक रूप से बहुत करीबी रिश्तेदार रहा है। इसमें कई समान चिंताओं हैं, और यह हास्केल से बहुत अलग है। हालांकि, यह सिर्फ सामान्य लिस्प का व्यवहार नहीं है। यह ऐसा कुछ है जो किसी भी रनटाइम के किसी भी डिजाइनर को किसी बिंदु पर मिलना चाहिए। यह भी कारण है कि आपको विशेष फ़ंक्शन का उपयोग करके जावा में स्ट्रिंग की तुलना क्यों करनी चाहिए, क्यों सी में शाब्दिक तारों के पॉइंटर्स की तुलना करना एक अच्छा विचार नहीं है। मैं जो कह रहा हूं वह यह है कि मेरे द्वारा वर्णित तरीके से चीजों का एक कारण है , और यदि वे अब नहीं हैं, तो वे भविष्य में हो सकते हैं।
जोड़ा लेखक Yann Trevin, स्रोत
@lunaryorn मैं कम परवाह नहीं कर सका।
जोड़ा लेखक Yann Trevin, स्रोत
मैं कर सकता हूं और मैं करूगा! मैंने मैक्रो का उपयोग करके आंशिक समाधान शामिल करने के लिए प्रश्न संपादित किया है, लेकिन यह अभी भी एक पूर्ण समाधान नहीं है।
जोड़ा लेखक Iker Jimenez, स्रोत
कस्टम फ़ंक्शंस में env को गुजरने में समस्या यह है कि उपयोगकर्ता को अपने कार्यों में डमी तर्क जोड़ने की आवश्यकता होती है, क्योंकि उन्हें foo < कोड> बार और baz , और मुझे लगता है कि यह उपयोगकर्ता के अनुकूल पर्याप्त नहीं है। हालांकि इसने मुझे उपयोगकर्ता को अज्ञात शब्दावली वातावरण पारित करने पर पुनर्विचार किया है। बुरा हो सकता है।
जोड़ा लेखक Iker Jimenez, स्रोत