2010-10-04 2 views
39

मुझे लगता है कि Practical Common Lisp का उपयोग वैश्विक चर सेट करने के लिए करता है। क्या एक ही उद्देश्य के लिए setq का उपयोग करना ठीक नहीं है?लिस्प में सेटक और डिफवर

defvar बनाम setq का उपयोग करने के फायदे/नुकसान क्या हैं?

उत्तर

38

वहाँ कई चर लागू करने के लिए तरीके हैं।

DEFVAR और DEFPARAMETERवैश्विक गतिशील चर परिचय। DEFVAR वैकल्पिक रूप से इसे कुछ मान पर सेट करता है, जब तक कि यह पहले से परिभाषित नहीं होता है। DEFPARAMETER इसे हमेशा प्रदान किए गए मान पर सेट करता है। SETQ एक चर लागू नहीं करता है।

(defparameter *number-of-processes* 10) 

(defvar *world* (make-world))  ; the world is made only once. 

सूचना है कि आप की संभावना कभी नहीं जैसे नामों के साथ DEFVAR चर करना चाहते x, y, stream, limit ... क्यों? क्योंकि इन चरों को तब विशेष घोषित किया जाएगा और इसे पूर्ववत करना मुश्किल होगा। विशेष घोषणा वैश्विक है और चर के सभी आगे उपयोग गतिशील बाध्यकारी का उपयोग करेंगे।

खराब:

(defvar x 10)  ; global special variable X, naming convention violated 
(defvar y 20)  ; global special variable Y, naming convention violated 

(defun foo() 
    (+ x y))  ; refers to special variables X and y 

(defun bar (x y) ; OOPS!! X and Y are special variables 
        ; even though they are parameters of a function! 
    (+ (foo) x y)) 

(bar 5 7)   ; -> 24 

बेहतर: हमेशा उनके नाम में * साथ विशेष चरों निशान!

(defvar *x* 10)  ; global special variable *X* 
(defvar *y* 20)  ; global special variable *Y* 

(defun foo() 
    (+ *x* *y*))  ; refers to special variables X and y 

(defun bar (x y) ; Yep! X and Y are lexical variables 
    (+ (foo) x y)) 

(bar 5 7)   ; -> 42 

स्थानीय चर DEFUN, LAMBDA, LET, MULTIPLE-VALUE-BIND और कई अन्य लोगों के साथ पेश कर रहे हैं।

(defun foo (i-am-a-local-variable) 
    (print i-am-a-local-variable)) 

(let ((i-am-also-a-local-variable 'hehe)) 
    (print i-am-also-a-local-variable)) 

अब, डिफ़ॉल्ट रूप से ऊपर दो रूपों में स्थानीय चर शाब्दिक, कर रहे हैं जब तक कि वे विशेष घोषित कर रहे हैं। फिर वे गतिशील चर होंगे।

अगला, एक चर को नए मानों में सेट करने के लिए कई रूप भी हैं।SET, SETQ, SETF और अन्य। SETQ और SETF दोनों लेक्सिकल और विशेष (गतिशील) चर सेट कर सकते हैं।

पोर्टेबल कोड के लिए यह आवश्यक है कि एक वेरिएबल सेट करता है जो पहले ही घोषित हो चुका है। घोषित चर सेट करने का सटीक प्रभाव मानक द्वारा अपरिभाषित है।

इसलिए, यदि आप जानते हैं कि आपके कॉमन लिस्प कार्यान्वयन क्या करता है, तो आप

(setq world (make-new-world)) 

पढ़ें-Eval-प्रिंट लूप में उच्चस्तरीय पर उपयोग कर सकते हैं। लेकिन इसे अपने कोड में उपयोग न करें, क्योंकि प्रभाव पोर्टेबल नहीं है। आमतौर पर SETQ चर सेट करेगा। लेकिन कुछ कार्यान्वयन वैरिएबल विशेष भी घोषित कर सकता है जब इसे यह नहीं पता (सीएमयू कॉमन लिस्प डिफ़ॉल्ट रूप से करता है)। यह लगभग हमेशा नहीं होता कि कोई क्या चाहता है। यदि आप जानते हैं कि आप क्या करते हैं, लेकिन कोड के लिए नहीं, तो आकस्मिक उपयोग के लिए इसका उपयोग करें।यहाँ

ही:

(defun make-shiny-new-world() 
    (setq world (make-world 'shiny))) 

सबसे पहले, इस तरह के चर *world* के रूप में (आसपास के * पात्रों के साथ) लिखा जाना चाहिए, स्पष्ट करना यह एक वैश्विक विशेष चर रहा है कि। दूसरा, इसे DEFVAR या DEFPARAMETER से पहले घोषित किया जाना चाहिए था।

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

+1

मुझे आश्चर्य है कि एक बार जब आपने वैरिएवर के साथ वैरिएबल घोषित किया है, तो आप एक ही नाम के साथ स्थानीय चर बनाने का कोई तरीका नहीं है, इसलिए (चलो ((x 1)) x) एक्स अप्रत्याशित परिणाम उत्पन्न कर सकता है यदि एक्स defvar द्वारा घोषित किया जाता है। – Ian

+1

@ian यह एक कारण है कि कई लोग "कान मफ्स" का उपयोग करते हैं (यानी, वे कभी भी '(defvar x foo)' का उपयोग नहीं करते हैं, वे '(defvar * x * foo)' का उपयोग करते हैं, इस तरह आप बहुत कम संभावना रखते हैं भूल करना। – Vatine

7

DEFVAR एक नया वेरिएबल स्थापित करता है। SETQ एक चर को असाइन करता है।

अधिकांश लिस्प कार्यान्वयन मैं अगर आप एक ऐसा वैरिएबल अभी तक अस्तित्व में नहीं है SETQ एक चेतावनी जारी करेगा का उपयोग किया है।

7

defvar और defparameter दोनों वैश्विक चर परिचय। केन नोट्स के रूप में, setq एक चर को असाइन करता है।

इसके अलावा, defvardefvar -ed पहले कुछ नहीं होगा। सेबेल ने बाद में किताब (अध्याय 6) में कहा: "व्यावहारिक रूप से बोलते हुए, आपको वेरिएबल्स को परिभाषित करने के लिए DEFVAR का उपयोग करना चाहिए जिसमें डेटा शामिल होगा जिसे आप रखना चाहते हैं, भले ही आपने वेरिएबल का उपयोग करने वाले स्रोत कोड में कोई परिवर्तन किया हो।"

http://www.gigamonkeys.com/book/variables.html

उदाहरण के लिए

, आप सरल डेटाबेस अध्याय में डेटाबेस के लिए एक वैश्विक *db* अगर:

(defvar *db* nil) 

... और आप आरईपीएल पर इसके साथ खेलना शुरू - जोड़ने, हटाने चीजें, इत्यादि - लेकिन फिर आप उस स्रोत फ़ाइल में परिवर्तन करते हैं जिसमें डिफवर फॉर्म होता है, उस फ़ाइल को पुनः लोड करने से *db* और आपके द्वारा किए गए सभी परिवर्तनों को मिटाया नहीं जाएगा ... मेरा मानना ​​है कि setq होगा, जैसा कि defparameter होगा। यदि मैं गलत हूं तो एक और अनुभवी लिस्पर कृपया मुझे सही करें।

16

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

;; dynamic variable sample 
> (defvar *x* 100) 
*X* 
> (defun fx() *x*) 
FX 
> (fx) 
100 
> (let ((*x* 500)) (fx)) ;; gets the value of *x* from the dynamic scope. 
500 
> (fx) ;; *x* now refers to the global binding. 
100 

;; example of using a lexical variable 
> (let ((y 200)) 
    (let ((fy (lambda() (format t "~a~%" y)))) 
    (funcall fy) ;; => 200 
    (let ((y 500)) 
     (funcall fy) ;; => 200, the value of lexically bound y 
     (setq y 500) ;; => y in the current environment is modified 
     (funcall fy)) ;; => 200, the value of lexically bound y, which was 
        ;; unaffected by setq 
    (setq y 500) => ;; value of the original y is modified. 
    (funcall fy))) ;; => 500, the new value of y in fy's defining environment. 

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

> (defun my-print (s) 
     (format *out* "~a~%" s)) 
MY-PRINT 
> (my-print "hello") 
hello 
> (let ((*out* some-stream)) 
    (my-print " cruel ")) ;; goes to some-stream 
> (my-print " world.") 
world 

शाब्दिक चर का उपयोग आम तौर पर है, जो देश के साथ वस्तुओं का अनुकरण करने की। पहले उदाहरण में, fy के बाध्यकारी वातावरण में परिवर्तनीय y प्रभावी रूप से उस कार्य का निजी राज्य बन गया।

defvar केवल एक चर के लिए एक मान असाइन करेगा यदि यह पहले से असाइन नहीं किया गया है।तो *x* के निम्नलिखित फिर से परिभाषा बंधन मूल में बदलाव नहीं होगा:

> (defvar *x* 400) 
*X* 
> *x* 
100 

हम setq का उपयोग करके *x* के लिए एक नया मान असाइन कर सकते हैं:

> (setq *x* 400) 
400 
> *x* 
400 
> (fx) 
400 
> (let ((*x* 500)) (fx)) ;; setq changed the binding of *x*, but 
         ;; its dynamic property still remains. 
500 
> (fx) 
400 
+4

दुर्भाग्य से यह गलत है। घोषित/परिभाषित चर पर एक (setq y 200) का सटीक प्रभाव अनिर्धारित नहीं है। सामान्य लिस्प में ग्लोबल लेक्सिकल वैरिएबल भी नहीं हैं। एसईटीक्यू एक चर सेट करता है। और कुछ नहीं। प्रदान किए गए चर के आधार पर या तो गतिशील चर या एक शब्दावली चर। एलआईटी बांधता है। एसईटीक्यू सेट –

+0

कोई भी फ़ंक्शन को परिभाषित नहीं कर सकता है CL: PRINT, क्योंकि वह नाम पहले से ही मानक फ़ंक्शन द्वारा लिया गया है। प्रारूपों को स्ट्रीम करने के लिए फॉर्मेट प्रिंट करें। –

+0

@Rainer त्रुटियों को इंगित करने के लिए धन्यवाद। मैंने जवाब अपडेट किया है। –

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