एक बचाव के बिना डिफावर स्कूपिंग अलग तरीके से काम क्यों करता है?


10

मान लीजिए कि मेरे पास एक फ़ाइल है, जिसका नाम है elisp-defvar-test.el:

;;; elisp-defvar-test.el ---  -*- lexical-binding: t -*- 

(defvar my-dynamic-var)

(defun f1 (x)
  "Should return X."
  (let ((my-dynamic-var x))
    (f2)))

(defun f2 ()
  "Returns the current value of `my-dynamic-var'."
  my-dynamic-var)

(provide 'elisp-dynamic-test)

;;; elisp-defvar-test.el ends here

मैं इस फाइल को लोड करता हूं और फिर स्क्रैच बफर में जाता हूं और रन करता हूं:

(setq lexical-binding t)
(f1 5)
(let ((my-dynamic-var 5))
  (f2))

(f1 5)उम्मीद के मुताबिक 5 रिटर्न देता है, यह दर्शाता है कि शरीर एक गतिशील रूप से स्कोप किए गए चर के रूप f1में व्यवहार कर रहा है my-dynamic-var, जैसा कि अपेक्षित था। हालांकि, अंतिम रूप इसके लिए एक शून्य-चर त्रुटि देता है my-dynamic-var, यह दर्शाता है कि यह इस चर के लिए शाब्दिक स्कोपिंग का उपयोग कर रहा है। ऐसा लगता है कि प्रलेखन के साथ बाधाओं पर defvar, जो कहता है:

defvarप्रपत्र भी "विशेष" के रूप में चर घोषणा करता है, यह हमेशा गतिशील रूप से बाध्य है ताकि भले ही lexical-bindingटी है।

यदि मैं defvarप्रारंभिक मूल्य की आपूर्ति करने के लिए परीक्षण फ़ाइल में फ़ॉर्म बदलता हूं , तो चर को हमेशा डायनामिक माना जाता है, जैसे कि दस्तावेज कहता है। क्या कोई यह समझा सकता है defvarकि उस चर की घोषणा करते समय एक प्रारंभिक मूल्य के साथ आपूर्ति की गई थी या नहीं, इसके द्वारा किसी चर का निर्धारण क्यों किया जाता है ?

यदि यह मायने रखता है, तो यहां त्रुटि बैकट्रेस है:

Debugger entered--Lisp error: (void-variable my-dynamic-var)
  f2()
  (let ((my-dynamic-var 5)) (f2))
  (progn (let ((my-dynamic-var 5)) (f2)))
  eval((progn (let ((my-dynamic-var 5)) (f2))) t)
  elisp--eval-last-sexp(t)
  eval-last-sexp(t)
  eval-print-last-sexp(nil)
  funcall-interactively(eval-print-last-sexp nil)
  call-interactively(eval-print-last-sexp nil nil)
  command-execute(eval-print-last-sexp)

4
मुझे लगता है कि बग # 18059 में चर्चा प्रासंगिक है।
तुलसी

महान प्रश्न, और हाँ, कृपया बग # 18059 की चर्चा देखें।
आकर्षित किया

मुझे लगता है, इसलिए ऐसा लगता है कि इस दस्तावेज़ को Emacs 26 में संबोधित करने के लिए अपडेट किया जाएगा।
रयान सी। थॉम्पसन

जवाबों:


8

क्यों दोनों के साथ अलग-अलग व्यवहार किया जाता है "क्योंकि यही हमें चाहिए था"। अधिक विशेष रूप से, एकल-तर्क रूप defvarएक लंबे समय से पहले दिखाई दिया था, लेकिन बाद में दूसरे की तुलना में और मूल रूप से एक "हैक" था चुप्पी संकलक चेतावनी: निष्पादन समय पर इसका कोई प्रभाव नहीं था, इसलिए "दुर्घटना" के रूप में इसका मतलब था (defvar FOO)केवल वर्तमान फ़ाइल पर लागू होने का मौन व्यवहार (चूंकि कंपाइलर को यह जानने का कोई तरीका नहीं था कि किसी अन्य फ़ाइल में इस तरह के एक डिफ़र्वर्स को निष्पादित किया गया था)।

जब lexical-bindingEmacs-24 में पेश किया गया था, तो हमने इस फॉर्म को फिर से इस्तेमाल करने का फैसला किया (defvar FOO), लेकिन इसका मतलब है कि अब इसका असर है।

आंशिक रूप से पिछले "केवल वर्तमान फ़ाइल को प्रभावित करता है" व्यवहार को प्रभावित करता है, लेकिन एक पुस्तकालय को totoडायनेमिक रूप से स्कोपेड वर्जन के रूप में उपयोग करने की अनुमति के बिना अन्य पुस्तकालयों को totoलेक्सिक रूप से स्कोपेड वर्जन के रूप में उपयोग करने से रोकने के लिए (आमतौर पर पैकेज-उपसर्ग नामकरण सम्मेलन उन लोगों से बचता है) संघर्ष, लेकिन इसका हर जगह दु: ख के साथ उपयोग नहीं किया जाता है), के नए व्यवहार को (defvar FOO)केवल वर्तमान फ़ाइल पर लागू करने के लिए परिभाषित किया गया था, और इसे परिष्कृत भी किया गया था, इसलिए यह केवल वर्तमान दायरे पर लागू होता है (उदाहरण के लिए यदि यह किसी फ़ंक्शन के भीतर दिखाई देता है, तो यह केवल इसके उपयोग को प्रभावित करता है उस कार्य के भीतर वह संस्करण)।

मौलिक रूप से, (defvar FOO VAL)और (defvar FOO)सिर्फ दो "पूरी तरह से अलग" चीजें हैं। वे सिर्फ ऐतिहासिक कारणों से एक ही कीवर्ड का उपयोग करने के लिए होते हैं।


1
जवाब के लिए +1। लेकिन आम लिस्प का दृष्टिकोण स्पष्ट और बेहतर है, आईएमएचओ।
आकर्षित किया

@ ड्रू: मैं ज्यादातर सहमत हूं, लेकिन (defvar FOO)नए मोड को पुराने कोड के साथ अधिक संगत बनाता है। इसके अलावा, कॉमन लिस्प के समाधान के साथ IIRC एक समस्या यह है कि यह एलिस्प की तरह एक शुद्ध-दुभाषिया के लिए बहुत महंगा है (उदाहरण के लिए, हर बार जब आप मूल्यांकन करते letहैं कि आपको इसके शरीर के अंदर देखना होगा, तो declareकुछ वैरियों को प्रभावित करता है)।
स्टीफन

दोनों की गिनती पर सहमत हुए।
आकर्षित किया

4

प्रयोग के आधार पर, मेरा मानना ​​है कि मुद्दा यह है कि (defvar VAR)बिना इनिट वैल्यू के केवल लाइब्रेरी (एस) पर इसका प्रभाव पड़ता है।

जब मैं जोड़ा (defvar my-dynamic-var)करने के लिए *scratch*बफर, त्रुटि नहीं रह गया है हुई।

मैंने मूल रूप से यह सोचा था कि यह उस फॉर्म का मूल्यांकन करने के कारण था , लेकिन मैंने पहली बार देखा कि बस उस फॉर्म के साथ फाइल का दौरा करना पर्याप्त था; और इसके अलावा कि बफर में उस फॉर्म को जोड़ने (या हटाने) के बिना, इसका मूल्यांकन किए बिना यह बदलने के लिए पर्याप्त था (let ((my-dynamic-var 5)) (f2))कि उसी बफर के अंदर मूल्यांकन करते समय क्या हुआ था eval-last-sexp

(मुझे इस बात की कोई वास्तविक समझ नहीं है कि यहां क्या हो रहा है। मैं व्यवहार को आश्चर्यचकित करता हूं, लेकिन इस कार्यक्षमता को कैसे कार्यान्वित किया जाता है, इसके विवरण से मैं परिचित नहीं हूं।)

मैं जोड़ना होगा कि के लिए इस प्रपत्र defvar(कोई init मूल्य के साथ) elisp फ़ाइल में एक बाहरी रूप से परिभाषित गतिशील चर का उपयोग करता है के बारे में शिकायत से बाइट संकलक से बचाता है संकलित किया जा रहा है, लेकिन अपने आप ही यह नहीं है कारण है कि चर होने के लिए boundp; इसलिए यह चर को सख्ती से परिभाषित नहीं कर रहा है। (ध्यान दें कि यदि चर था, boundp तो यह समस्या बिल्कुल नहीं होगी।)

व्यवहार में मैं यह ठीक बाहर काम करने के बशर्ते कि आप जा रहा है लगता है है शामिल (defvar my-dynamic-var)किसी भी शाब्दिक बाध्यकारी पुस्तकालय जो अपने उपयोग करता में my-dynamic-varचर (जो संभवतः एक असली परिभाषा कहीं और होता है)।


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

टिप्पणियों में @npostavs से सूचक के लिए धन्यवाद:

दोनों eval-last-sexpऔर क्रम में eval-defunउपयोग eval-sexp-add-defvars:

defvarयह सब बफर में पूर्ववर्ती के साथ EXP प्रस्तुत करना ।

विशेष रूप से यह सब स्थित defvar, defconst, और defcustomउदाहरणों। (जब भी टिप्पणी की जाती है, मैं नोटिस करता हूं।)

जैसा कि यह कॉल-टाइम पर बफर को खोज रहा है, यह बताता है कि इन रूपों का मूल्यांकन किए बिना भी बफर में प्रभाव कैसे हो सकता है, और पुष्टि करता है कि प्रपत्र एक ही elisp फ़ाइल में प्रकट होना चाहिए (और कोड का मूल्यांकन किए जाने से पहले भी) ।


2
IIUC , बग # 18059 आपके विस्तार की पुष्टि करता है।
तुलसी

2
ऐसा लगता है जो eval-sexp-add-defvarsबफर टेक्स्ट में डिफार्स के लिए जाँच करता है।
npostavs

1
+1। स्पष्ट रूप से यह सुविधा स्पष्ट नहीं है, या उपयोगकर्ताओं को स्पष्ट रूप से प्रस्तुत नहीं की गई है। बग # 18059 के लिए डॉक फिक्स मदद करता है, लेकिन यह अभी भी कुछ रहस्यमय है, अगर उपयोगकर्ताओं के लिए नाजुक नहीं है।
आकर्षित किया

0

मैं इसे बिल्कुल भी पुन: पेश नहीं कर सकता, बाद के स्निपेट का मूल्यांकन करना यहां ठीक काम करता है और उम्मीद के मुताबिक 5 रिटर्न देता है। क्या आप निश्चित हैं कि आप my-dynamic-varइसका मूल्यांकन नहीं कर रहे हैं ? यह एक त्रुटि फेंक देगा क्योंकि चर शून्य है, यह एक मूल्य पर सेट नहीं किया गया है और यह केवल एक होगा यदि आप इसे गतिशील रूप से एक से बाध्य करते हैं।


1
क्या आपने lexical-bindingरूपों का मूल्यांकन करने से पहले गैर-शून्य सेट किया था ? मुझे आपके द्वारा वर्णित व्यवहार मिल जाता है lexical-binding, लेकिन जब मैं इसे गैर-शून्य पर सेट करता हूं, तो मुझे शून्य-चर त्रुटि मिलती है।
रयान सी। थॉम्पसन

हां, मैंने इसे एक अलग फाइल में सेव किया, रिवर्ट किया, चेक किया जो lexical-bindingसेट है और क्रमिक रूप से मूल्यांकन करता है।
वामासा

@wasamasa मेरे लिए पुन: प्रस्तुत करता है, हो सकता है कि आपने गलती my-dynamic-varसे अपने वर्तमान सत्र में एक शीर्ष-स्तरीय गतिशील मूल्य दिया हो ? मुझे लगता है कि यह स्थायी रूप से विशेष रूप से चिह्नित कर सकता है।
npostavs 13
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.