2009-01-20 20 views
35

में गतिशील और लेक्सिकल चर, मैं पीटर सेबेल द्वारा 'प्रैक्टिकल कॉमन लिस्प' पुस्तक पढ़ रहा हूं।सामान्य लिस्प

अध्याय 6 में, "चर" खंड "लेक्सिकल वैरिएबल और क्लोजर" और "गतिशील, ए.के.ए. स्पेशल, वेरिएबल्स"। http://www.gigamonkeys.com/book/variables.html

मेरे समस्या यह है कि दोनों वर्गों में उदाहरणों से पता चलता है कि कैसे (हैं ...) वैश्विक चर शैडो कर सकते हैं और वास्तव में गतिशील और शाब्दिक वार्स के बीच अंतर नहीं बताता है।

मैं समझता हूँ कि कैसे बंद काम करते हैं लेकिन मैं वास्तव में क्या इतना बारे में इस उदाहरण में जाने विशेष नहीं मिलता:

(defvar *x* 10) 

(defun foo() 
    (format t "Before assignment~18tX: ~d~%" *x*) 
    (setf *x* (+ 1 *x*)) 
    (format t "After assignment~18tX: ~d~%" *x*)) 


(defun bar() 
    (foo) 
    (let ((*x* 20)) (foo)) 
    (foo)) 


CL-USER> (foo) 
Before assignment X: 10 
After assignment X: 11 
NIL 


CL-USER> (bar) 
Before assignment X: 11 
After assignment X: 12 
Before assignment X: 20 
After assignment X: 21 
Before assignment X: 12 
After assignment X: 13 
NIL 

मुझे लगता है विशेष यहाँ पर जा रहा है कोई बात नहीं है की तरह। बार में बाहरी foo वैश्विक एक्स वृद्धि कर देता है, और fooसे घिरा हुआ बार वृद्धि कर देता है छाया एक्स में करते हैं। क्या बड़ी बात है? मैं नहीं देखता कि यह व्याख्यात्मक और गतिशील वार्स के बीच अंतर को समझाने के लिए कैसे किया जाता है। फिर भी पुस्तक इस तरह जारी है:

तो यह कैसे काम करता है? LET कैसे पता चलता है कि जब यह x बांधता है तो यह सामान्य लेक्सिकल बाध्यकारी के बजाय गतिशील बाध्यकारी बनाना चाहता है? यह जानता है क्योंकि नाम घोषित किया गया है .1.1 प्रत्येक का नाम DEFVAR और DEFPARAMETER के साथ परिभाषित चर स्वचालित रूप से वैश्विक रूप से घोषित किया गया है।

अगर जानेएक्स बाध्य होगा "सामान्य शाब्दिक बंधन" का उपयोग कर क्या whould? सब कुछ, गतिशील और शब्दावली बाध्यकारी के बीच अंतर क्या हैं और गतिशील बाध्यकारी के संबंध में यह उदाहरण विशेष कैसे है?

उत्तर

23

जब एक चर है lexically scoped, प्रणाली जहां समारोह परिभाषित एक नि: शुल्क चर के लिए मूल्य मिल रहा है करने के लिए लग रहा है। जब एक चर गतिशील रूप से स्कॉप्ड होता है, तो यह सिस्टम देखता है कि फ़ंक्शन कहलाता है जहां को फ्री वैरिएबल के मान को खोजने के लिए कहा जाता है। सामान्य लिस्प में चर सभी डिफ़ॉल्ट रूप से व्याख्यात्मक होते हैं; हालांकि, defvar या defparameter का उपयोग करके गतिशील रूप से स्कॉप्ड चर को शीर्ष स्तर पर परिभाषित किया जा सकता है।

एक सरल उदाहरण

(setq के साथ) शाब्दिक scoping:

(setq x 3) 

(defun foo() x) 

(let ((x 4)) (foo)) ; returns 3 

(defvar के साथ) गतिशील scoping:

(defvar x 3) 

(defun foo() x) 

(let ((x 4)) (foo)) ; returns 4 

हैं कैसे पता है यदि एक चर लेक्सिकल या गतिशील है? यह नहीं करता है। दूसरी तरफ, जब एक्स एक्स के मान को खोजने के लिए जाता है, तो शुरुआत में इसे शीर्ष स्तर पर परिभाषित लेक्सिकल मान मिल जाएगा। यह तब जांचता है कि चर को गतिशील माना जाता है या नहीं। यदि ऐसा है, तो foo कॉलिंग वातावरण को देखता है, जो इस मामले में, एक्स के मान को 4 ओवर करने के लिए उपयोग करता है।

(नोट: यह एक ओवरम्प्लिफिकेशन है, लेकिन यह कल्पना करने में मदद करेगा विभिन्न स्कोपिंग नियमों के बीच अंतर)

+9

आम लिस्प में कोई व्याख्यात्मक वैश्विक चर नहीं है। कार्यान्वयन के आधार पर एसईटीक्यू के साथ आपका उदाहरण काम कर सकता है या नहीं भी हो सकता है। –

+1

क्यों कोई वैश्विक शब्दावली नहीं? ऐसा लगता है कि ज्यादा समझ में नहीं आता है। –

+19

आश्चर्य की बात है? लेकिन ऐसा ही है। एएनएसआई कॉमन लिस्प के मानक में कोई वैश्विक शब्दावली चर नहीं है। ** आपकी व्याख्या अधिकतर गलत है **। एलईटी जानता है कि एक चर लेक्सिकल है या नहीं। यह डिफ़ॉल्ट है और चर को लेक्सिकल वातावरण में परिभाषित किया जाना है। यह भी जानता है कि यह कब विशेष है, क्योंकि वहां 'विशेष' घोषणा की आवश्यकता है। डेफवर एक है। यदि आपका चरणीय नहीं है और विशेष घोषित नहीं किया गया है, तो लिस्प प्रणाली सभी प्रकार की धारणाओं को मुक्त करने के लिए स्वतंत्र है। आम लिस्प मानक यह नहीं कहता कि इसे कैसे काम करना चाहिए। एक कार्यान्वयन किसी भी तरह से इसका सामना करेगा। –

8

शायद यह उदाहरण मदद करेगा।

;; the lexical version 

(let ((x 10)) 
    (defun lex-foo() 
    (format t "Before assignment~18tX: ~d~%" x) 
    (setf x (+ 1 x)) 
    (format t "After assignment~18tX: ~d~%" x))) 

(defun lex-bar() 
    (lex-foo) 
    (let ((x 20)) ;; does not do anything 
    (lex-foo)) 
    (lex-foo)) 

;; CL-USER> (lex-bar) 
;; Before assignment X: 10 
;; After assignment X: 11 
;; Before assignment X: 11 
;; After assignment X: 12 
;; Before assignment X: 12 
;; After assignment X: 13 

;; the dynamic version 

(defvar *x* 10) 
(defun dyn-foo() 
    (format t "Before assignment~18tX: ~d~%" *x*) 
    (setf *x* (+ 1 *x*)) 
    (format t "After assignment~18tX: ~d~%" *x*)) 

(defun dyn-bar() 
    (dyn-foo) 
    (let ((*x* 20)) 
    (dyn-foo)) 
    (dyn-foo)) 

;; CL-USER> (dyn-bar) 
;; Before assignment X: 10 
;; After assignment X: 11 
;; Before assignment X: 20 
;; After assignment X: 21 
;; Before assignment X: 11 
;; After assignment X: 12 

;; the special version 

(defun special-foo() 
    (declare (special *y*)) 
    (format t "Before assignment~18tX: ~d~%" *y*) 
    (setf *y* (+ 1 *y*)) 
    (format t "After assignment~18tX: ~d~%" *y*)) 

(defun special-bar() 
    (let ((*y* 10)) 
    (declare (special *y*)) 
    (special-foo) 
    (let ((*y* 20)) 
     (declare (special *y*)) 
     (special-foo)) 
    (special-foo))) 

;; CL-USER> (special-bar) 
;; Before assignment X: 10 
;; After assignment X: 11 
;; Before assignment X: 20 
;; After assignment X: 21 
;; Before assignment X: 11 
;; After assignment X: 12 
7

आप स्थानीय चर गतिशील भी बाध्य करने के लिए अपने लिस्प बता सकते हैं:

(let ((dyn 5)) 
    (declare (special dyn)) 
    ... ;; DYN has dynamic scope for the duration of the body 
) 
44

पर क्या हो रहा है?

आप कहते हैं: ऐसा लगता है कि यहां कुछ खास नहीं चल रहा है। foobar में वैश्विक x बढ़ता है, और fooletbar से घिरा हुआ x छायांकित करता है। क्या बड़ी बात है?

विशेष कि यहां हो रहा है कि LET*x* का मूल्य शैडो कर सकते हैं। शब्दावली चर के साथ जो संभव नहीं है।

कोड वाणी *x*DEFVAR के माध्यम से विशेष किया जाना है।

FOO में अब *x* का मान गतिशील दिखाई देता है। FOO वर्तमान गतिशील बाध्यकारी*x* या यदि कोई नहीं है, तो प्रतीक *x* का प्रतीक मान लेगा। एक नया गतिशील बाध्यकारी, उदाहरण के लिए, LET के साथ पेश किया जा सकता है।

दूसरी ओर एक अक्षीय चर कहीं भी लेक्सिकल वातावरण में मौजूद होना चाहिए। LET, LAMBDA, DEFUN और अन्य ऐसे व्याख्यात्मक चर प्रस्तुत कर सकते हैं।यहाँ देखें शाब्दिक चर x तीन अलग अलग तरीकों में पेश किया:

(let ((x 3)) 
    (* (sin x) (cos x))) 

(lambda (x) 
    (* (sin x) (cos x))) 

(defun baz (x) 
    (* (sin x) (cos x))) 

यदि हमारे कोड थे:

(defvar x 0) 

(let ((x 3)) 
    (* (sin x) (cos x))) 

(lambda (x) 
    (* (sin x) (cos x))) 

(defun baz (x) 
    (* (sin x) (cos x))) 

फिर X, विशेष थे DEFVAR घोषणा, की वजह से तीनों ऊपर मामलों में जो X घोषित विशेष - वैश्विक स्तर पर सभी स्तरों के लिए घोषित करता है। इस वजह से, विशेष चर *X* के रूप में घोषित करने के लिए सम्मेलन है। इस प्रकार उनके चारों ओर सितारों के साथ केवल चर विशेष - सम्मेलन द्वारा हैं। यह एक उपयोगी सम्मेलन है।

अपने कोड में आप तो है:

(defun bar() 
    (foo) 
    (let ((*x* 20)) 
    (foo)) 
    (foo)) 

*x* के बाद घोषित किया गया है अपने कोड में ऊपर DEFVAR के माध्यम से विशेष, LET निर्माण एक नए गतिशील बंधन *x* के लिए परिचय देता है। FOO को तब बुलाया जाता है। चूंकि FOO*x*गतिशील बाध्यकारी का उपयोग करता है, यह वर्तमान में दिखता है और यह पाया जाता है कि *x* गतिशील रूप से 20 पर बाध्य है।

विशेष चर का मान वर्तमान गतिशील बाध्यकारी में पाया जाता है।

स्थानीय विशेष घोषणाओं

वहाँ भी स्थानीय special घोषणाओं हैं: चर एक DEFVAR या DEFPARAMETER द्वारा घोषित किया गया था, तो विशेष

(defun foo-s() 
    (declare (special *x*)) 
    (+ *x* 1)) 

, तो स्थानीय special घोषणा छोड़ा जा सकता है ।

(defun foo-l (x) 
    (+ x 1)) 

की यह व्यवहार में देखते हैं:

(let ((f (let ((x 10)) 
      (lambda() 
      (setq x (+ x 1)))))) 
    (print (funcall f)) ; form 1 
    (let ((x 20))   ; form 2 
    (print (funcall f)))) 

यहाँ सभी चर शाब्दिक हैं

एक शाब्दिक चर सीधे चर बाध्यकारी संदर्भ देता है। फॉर्म 2LET हमारे फ़ंक्शन f में X को छाया नहीं देगा। यह नहीं कर सकता समारोह LET ((X 10) द्वारा पेश किए गए लेक्सिकल बाध्य चर का उपयोग करता है। फॉर्म 2 में किसी अन्य व्याख्यात्मक बाध्य X के साथ कॉल के आस-पास हमारे फ़ंक्शन पर कोई प्रभाव नहीं पड़ता है।

के विशेष चर कोशिश करते हैं:

(let ((f (let ((x 10)) 
      (declare (special x)) 
      (lambda() 
      (setq x (+ x 1)))))) 
    (print (funcall f)) ; form 1 
    (let ((x 20))   ; form 2 
    (declare (special x)) 
    (print (funcall f)))) 

मैं क्या करूं? क्या वह काम करता है?

ऐसा नहीं है!

पहला फॉर्म फ़ंक्शन को कॉल करता है और यह X के गतिशील मान को देखने का प्रयास करता है और कोई भी नहीं है। हमें फॉर्म 1: X में कोई त्रुटि नहीं मिलती है, क्योंकि प्रभाव में कोई गतिशील बाध्यकारी नहीं है।

फॉर्म 2 काम करेगा, क्योंकि special घोषणा के साथ LET एक गतिशील X के लिए बाध्यकारी परिचय देता है।

+5

बहुत बढ़िया स्पष्टीकरण है! हम तुम्हारे बिना क्या करते हैं? –

5

पीसीएल से उदाहरण लिखें।

;;; Common Lisp is lexically scoped by default. 

λ (setq x 10) 
=> 10 

λ (defun foo() 
    (setf x (1+ x))) 
=> FOO 

λ (foo) 
=> 11 

λ (let ((x 20)) 
    (foo)) 
=> 12 

λ (proclaim '(special x)) 
=> NIL 

λ (let ((x 20)) 
    (foo)) 
=> 21 

फिर भी On Lisp से दूसरे महान विवरण, अध्याय 2.5 स्कोप:

कॉमन लिस्प एक lexically scoped लिस्प है। योजना शब्दावली के दायरे के साथ सबसे पुरानी बोली है; योजना से पहले, गतिशील क्षेत्र को लिस्प की परिभाषित विशेषताओं में से एक माना जाता था।

लेक्सिकल और गतिशील दायरे के बीच का अंतर यह है कि कार्यान्वयन कैसे मुक्त चर के साथ सौदा करता है। एक प्रतीक अभिव्यक्ति में बंधे हैं यदि इसे एक चर के रूप में स्थापित किया गया है, या तो पैरामीटर के रूप में दिखाई दे रहा है, या चर-बाध्यकारी ऑपरेटरों द्वारा चलो और करते हैं। जो प्रतीक बंधे नहीं हैं उन्हें मुक्त माना जाता है। इस उदाहरण में, दायरा खेल में आता है:

(let ((y 7)) 
    (defun scope-test (x) 
    (list x y))) 

डिफ्यून अभिव्यक्ति के भीतर, एक्स बाध्य है और वाई निःशुल्क है। नि: शुल्क चर दिलचस्प हैं क्योंकि यह स्पष्ट नहीं है कि उनके मूल्य क्या होना चाहिए। बाध्य चर के मूल्य के बारे में कोई अनिश्चितता नहीं है- जब स्कोप-टेस्ट कहा जाता है, तो x का मान तर्क के रूप में जो कुछ भी पारित किया जाना चाहिए। लेकिन वाई का मूल्य क्या होना चाहिए? बोली के दायरे के नियमों का उत्तर यही है।

एक गतिशील रूप से स्कॉप्ड लिस्प में, एक मुक्त चर के मूल्य को खोजने के लिए, जब स्कोप-टेस्ट का निष्पादन होता है, तो हम इसे क्रियाओं की श्रृंखला के माध्यम से वापस देखते हैं। जब हमें एक पर्यावरण मिल जाता है जहां y बाध्य था, तो वाई का बाध्यकारी स्कोप-टेस्ट में उपयोग किया जाएगा। अगर हमें कोई नहीं मिलता है, तो हम वाई का वैश्विक मूल्य लेते हैं। इस प्रकार, एक गतिशील scoped लिस्प में, वाई मूल्य इसे बुला अभिव्यक्ति में पड़ा होगा:

> (let ((y 5)) (scope-test 3)) 
    (3 5) 
गतिशील गुंजाइश के साथ

, यह कुछ भी कि y 7 करने के लिए बाध्य किया गया था जब गुंजाइश परीक्षण परिभाषित किया गया था मतलब है। यह सब मायने रखता है कि वाई के पास 5 का मूल्य था जब स्कोप-टेस्ट कहा जाता था।

कॉलिंग कार्यों की श्रृंखला के माध्यम से वापस देखने की बजाय, एक व्याख्यात्मक रूप से स्कॉप्ड लिस्प में, हम कार्य परिभाषित किए जाने के दौरान युक्त वातावरण के माध्यम से वापस देख रहे हैं। एक व्याख्यात्मक रूप से लिपटे लिस्प में, हमारा उदाहरण वाई के बाध्यकारी को पकड़ लेगा जहां स्कोप-टेस्ट परिभाषित किया गया था। तो यह क्या कॉमन लिस्प में क्या होगा यह है:

> (let ((y 5)) (scope-test 3)) 
    (3 7) 

यहाँ कॉल के समय 5 करने के लिए y के बंधन दिए गए मान पर कोई प्रभाव नहीं है।

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

+1

'(घोषणा' (विशेष प्रतीक) 'द्वारा, आप दावा कर सकते हैं कि एक प्रतीक विशेष है। – juanitofatas

संबंधित मुद्दे