खोज…


टिप्पणियों

मैक्रोज़ का उद्देश्य

मैक्रोज़ का उद्देश्य कोड बनाना, कोड बदलना और नई सूचनाएं प्रदान करना है। ये नई सूचनाएं कार्यक्रम को बेहतर ढंग से व्यक्त करने के लिए अधिक अनुकूल हो सकती हैं, उदाहरण के लिए डोमेन-स्तर की निर्माण या संपूर्ण नई एम्बेडेड भाषाओं को प्रदान करके।

मैक्रोज़ सोर्स कोड को अधिक आत्म-व्याख्यात्मक बना सकते हैं, लेकिन डिबगिंग को और अधिक कठिन बना सकते हैं। अंगूठे के एक नियम के रूप में, किसी को मैक्रोज़ का उपयोग नहीं करना चाहिए जब एक नियमित कार्य करेगा। जब आप उनका उपयोग करते हैं, तो सामान्य नुकसान से बचें, आमतौर पर उपयोग किए जाने वाले पैटर्न और नामकरण सम्मेलनों से चिपके रहने का प्रयास करें।

स्थूल विस्तार आदेश

फ़ंक्शंस की तुलना में, मैक्रोज़ एक रिवर्स ऑर्डर में विस्तारित होते हैं; सबसे पहले, सबसे आखिरी में। इसका मतलब है कि डिफ़ॉल्ट रूप से कोई बाहरी मैक्रो के लिए आवश्यक सिंटैक्स उत्पन्न करने के लिए एक आंतरिक मैक्रो का उपयोग नहीं कर सकता है।

मूल्यांकन आदेश

कभी-कभी मैक्रोज़ को उपयोगकर्ता-आपूर्ति किए गए फ़ॉर्म को चारों ओर ले जाने की आवश्यकता होती है। यह सुनिश्चित करना चाहिए कि जिस क्रम में उनका मूल्यांकन किया गया है उसे न बदलें। उपयोगकर्ता क्रम में हो रहे दुष्प्रभावों पर निर्भर हो सकता है।

केवल एक बार मूल्यांकन करें

एक मैक्रो के विस्तार को अक्सर एक ही उपयोगकर्ता-आपूर्ति किए गए फॉर्म के मूल्य का एक से अधिक बार उपयोग करने की आवश्यकता होती है। यह संभव है कि फॉर्म के साइड-इफेक्ट्स हों, या यह एक महंगा कार्य कह रहा हो। इस प्रकार मैक्रो को केवल एक बार इस तरह के रूपों का मूल्यांकन करना सुनिश्चित करना चाहिए। आमतौर पर यह एक स्थानीय वैरिएबल (जिसका नाम GENSYM ed है) के मान को असाइन करके किया जाएगा।

EVAL-WHEN का उपयोग करते हुए मैक्रोज़ द्वारा उपयोग किए गए कार्य

कॉम्प्लेक्स मैक्रोज़ में अक्सर अलग-अलग कार्यों में कार्यान्वित उनके तर्क के कुछ हिस्से होते हैं। हालांकि, यह याद रखना चाहिए कि वास्तविक कोड संकलित करने से पहले मैक्रोज़ का विस्तार किया जाता है। जब कोई फ़ाइल संकलित करता है, तो डिफ़ॉल्ट रूप से, उसी फ़ाइल में परिभाषित फ़ंक्शन और चर मैक्रो निष्पादन के दौरान उपलब्ध नहीं होंगे। सभी फ़ंक्शन और वैरिएबल परिभाषाएँ, एक ही फ़ाइल में, मैक्रो द्वारा उपयोग की जाने वाली एक EVAL-WHEN -form के अंदर लपेटी जानी चाहिए। EVAL-WHEN में सभी तीन बार निर्दिष्ट होना चाहिए, जब संलग्न कोड का मूल्यांकन भी लोड और रनटाइम के दौरान किया जाना चाहिए।

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun foobar () ...))

यह मैक्रो के विस्तार से बुलाए गए फ़ंक्शंस पर लागू नहीं होता है, केवल मैक्रो द्वारा बुलाए गए लोगों के लिए।

आम मैक्रो पैटर्न

TODO: शायद स्पष्टीकरण को टिप्पणियों में स्थानांतरित करें और उदाहरणों को अलग से जोड़ें

foof

आम लिस्प में, सामान्यीकृत संदर्भों की एक अवधारणा है। वे प्रोग्रामर को विभिन्न "स्थानों" पर मान सेट करने की अनुमति देते हैं जैसे कि वे चर थे। मैक्रो जो इस क्षमता का उपयोग करते हैं, उनके नाम में अक्सर एक F -postfix होता है। जगह आमतौर पर मैक्रो के लिए पहला तर्क है।

मानक से उदाहरण: INCF , DECF , ROTATEF , SHIFTF , REMF

एक मूर्खतापूर्ण उदाहरण, एक मैक्रो जो एक जगह में एक नंबर स्टोर के संकेत को फ़्लिप करता है:

(defmacro flipf (place)
  `(setf ,place (- ,place)))

साथ-FOO

मैक्रो जो एक संसाधन को प्राप्त करते हैं और सुरक्षित रूप से जारी करते हैं, उन्हें आमतौर पर एक WITH- साथ नाम दिया जाता है। मैक्रो को आमतौर पर सिंटैक्स का उपयोग करना चाहिए:

(with-foo (variable details-of-the-foo...)
  body...)

मानक से उदाहरण: WITH-OPEN-FILE , WITH-OPEN-STREAM , WITH-INPUT-FROM-STRING , WITH-OUTPUT-TO-STRING

इस प्रकार के मैक्रो को लागू करने के लिए एक दृष्टिकोण जो नाम प्रदूषण के कुछ नुकसान से बच सकता है और अनपेक्षित रूप से कई मूल्यांकन पहले एक कार्यात्मक संस्करण को लागू करने से है। उदाहरण के लिए, with-widget मैक्रो को लागू करने में पहला कदम जो सुरक्षित रूप से एक विजेट बनाता है और बाद में साफ हो जाता है एक फ़ंक्शन हो सकता है:

(defun call-with-widget (args function)
  (let ((widget (apply #'make-widget args))) ; obtain WIDGET
    (unwind-protect (funcall function widget)  ; call FUNCTION with WIDGET
      (cleanup widget)                         ; cleanup

क्योंकि यह एक फ़ंक्शन है, फ़ंक्शन या आपूर्तिकर्ता के नाम के दायरे के बारे में कोई चिंता नहीं है, और इससे संबंधित मैक्रो लिखना आसान होता है:

(defmacro with-widget ((var &rest args) &body body)
  `(call-with-widget (list ,@args) (lambda (,var) ,@body)))

डीओ-FOO

मैक्रोज़ कि कुछ पर पुनरावृति अक्सर DO -प्रिफ़िक्स के साथ नामित की जाती है। मैक्रो सिंटैक्स आमतौर पर फॉर्म में होना चाहिए

(do-foo (variable the-foo-being-done return-value)
  body...)

मानक से उदाहरण: DOTIMES , DOLIST , DO-SYMBOLS DOLIST

ध्यान से, EFOOCASE, CFOOCASE

मैक्रोज़ जो कुछ मामलों के खिलाफ एक इनपुट से मेल खाते हैं, उन्हें अक्सर CASE -postfix के साथ नामित किया जाता है। अक्सर एक E...CASE -variant होता है, जो एक त्रुटि का संकेत देता है यदि इनपुट किसी भी मामले से मेल नहीं खाता है, और C...CASE , जो एक निरंतर त्रुटि का संकेत देता है। उनमें सिंटेक्स जैसा होना चाहिए

(foocase input
  (case-to-match-against (optionally-some-params-for-the-case)
   case-body-forms...)
  more-cases...
  [(otherwise otherwise-body)])

मानक से उदाहरण: CASE , TYPECASE , TYPECASE HANDLER-CASE

उदाहरण के लिए, एक मैक्रो जो नियमित अभिव्यक्तियों के खिलाफ एक स्ट्रिंग से मेल खाता है और रजिस्टर समूहों को चर में बांधता है। नियमित अभिव्यक्तियों के लिए CL-PPCRE का उपयोग करता है।

(defmacro regexcase (input &body cases)
  (let ((block-sym (gensym "block"))
        (input-sym (gensym "input")))
    `(let ((,input-sym ,input))
       (block ,block-sym
         ,@(loop for (regex vars . body) in cases
                 if (eql regex 'otherwise)
                   collect `(return-from ,block-sym (progn ,vars ,@body))
                 else
                   collect `(cl-ppcre:register-groups-bind ,vars
                                (,regex ,input-sym)
                              (return-from ,block-sym
                                (progn ,@body))))))))

(defun test (input)
  (regexcase input
    ("(\\d+)-(\\d+)" (foo bar)
      (format t "Foo: ~a, Bar: ~a~%" foo bar))
    ("Foo: (\\w+)$" (foo)
      (format t "Foo: ~a.~%" foo))
    (otherwise (format t "Didn't match.~%"))))

(test "asd 23-234 qwe")
; Foo: 23, Bar: 234
(test "Foo: Foobar")
; Foo: Foobar.
(test "Foo: 43 - 23")
; Didn't match.

DEFINE-FOO, DEFFOO

चीजों को परिभाषित करने वाले मैक्रोज़ को आमतौर पर DEFINE- या DEF -prefix के साथ नाम दिया जाता है।

मानक से उदाहरण: DEFUN , DEFMACRO , DEFMACRO DEFINE-CONDITION

एनाफोरिक मैक्रोज़

एक एनैफोरिक मैक्रो एक मैक्रो है जो एक चर (अक्सर IT ) का परिचय देता है जो उपयोगकर्ता द्वारा आपूर्ति किए गए फॉर्म के परिणाम को कैप्चर करता है। एक सामान्य उदाहरण एनोफोरिक इफ है, जो एक नियमित IF तरह है, लेकिन परीक्षण-फॉर्म के परिणाम को संदर्भित करने के लिए चर IT को भी परिभाषित करता है।

(defmacro aif (test-form then-form &optional else-form)
  `(let ((it ,test-form))
     (if it ,then-form ,else-form)))

(defun test (property plist)
  (aif (getf plist property)
       (format t "The value of ~s is ~a.~%" property it)
       (format t "~s wasn't in ~s!~%" property plist)))

(test :a '(:a 10 :b 20 :c 30))
; The value of :A is 10.
(test :d '(:a 10 :b 20 :c 30))
; :D wasn't in (:A 10 :B 20 :C 30)!

MACROEXPAND

मैक्रो विस्तार मैक्रो को वास्तविक कोड में बदलने की प्रक्रिया है। यह आमतौर पर संकलन प्रक्रिया के हिस्से के रूप में होता है। संकलक वास्तव में संकलन कोड से पहले सभी मैक्रो रूपों का विस्तार करेगा। लिस्प कोड की व्याख्या के दौरान मैक्रो का विस्तार भी होता है।

मैक्रो फॉर्म को विस्तार से देखने के लिए कोई MACROEXPAND मैन्युअल रूप से कॉल कर सकता है।

CL-USER> (macroexpand '(with-open-file (file "foo")
                        (do-something-with file)))
(LET ((FILE (OPEN "foo")) (#:G725 T))
  (UNWIND-PROTECT
      (MULTIPLE-VALUE-PROG1 (PROGN (DO-SOMETHING-WITH FILE)) (SETQ #:G725 NIL))
    (WHEN FILE (CLOSE FILE :ABORT #:G725))))

MACROEXPAND-1 एक ही है, लेकिन केवल एक बार फैलता है। एक स्थूल रूप की समझ बनाने की कोशिश करते समय यह उपयोगी है जो दूसरे स्थूल रूप में फैलता है।

CL-USER> (macroexpand-1 '(with-open-file (file "foo")
                          (do-something-with file)))
(WITH-OPEN-STREAM (FILE (OPEN "foo")) (DO-SOMETHING-WITH FILE))

ध्यान दें कि सभी स्तरों पर न तो MACROEXPAND और न ही MACROEXPAND-1 लिस्प कोड का विस्तार करें। वे केवल शीर्ष-स्तरीय मैक्रो प्रपत्र का विस्तार करते हैं। सभी स्तरों पर पूरी तरह से एक फॉर्म को मैक्रोफैक्जैंड करने के लिए, किसी को ऐसा करने के लिए एक कोड वॉकर की आवश्यकता होती है। यह सुविधा आम लिस्प मानक में प्रदान नहीं की गई है।

Backquote - मैक्रोज़ के लिए कोड टेम्प्लेट लिखना

मैक्रों रिटर्न कोड। चूंकि लिस्प में कोड सूचियों के होते हैं, इसलिए कोई भी इसे उत्पन्न करने के लिए नियमित सूची हेरफेर कार्यों का उपयोग कर सकता है।

;; A pointless macro
(defmacro echo (form)
  (list 'progn
        (list 'format t "Form: ~a~%" (list 'quote form))
        form))

यह अक्सर पढ़ने के लिए बहुत कठिन है, खासकर लंबे समय तक मैक्रोज़ में। Backquote रीडर मैक्रो किसी को उद्धृत टेम्प्लेट लिखने की अनुमति देता है जो चुनिंदा मूल्यांकन तत्वों द्वारा भरे जाते हैं।

(defmacro echo (form)
  `(progn
     (format t "Form: ~a~%" ',form)
     ,form))

(macroexpand '(echo (+ 3 4)))
;=> (PROGN (FORMAT T "Form: ~a~%" '(+ 3 4)) (+ 3 4))

यह संस्करण लगभग नियमित कोड जैसा दिखता है। कॉमा का उपयोग FORM का मूल्यांकन करने के लिए किया जाता है; बाकी सब कुछ जैसा है वैसा ही लौटा दिया जाता है। ध्यान दें कि ',form एकल उद्धरण अल्पविराम से बाहर है, इसलिए इसे वापस कर दिया जाएगा।

एक का उपयोग कर सकते हैं ,@ स्थिति में एक सूची को विभाजित करने के लिए।

(defmacro echo (&rest forms)
  `(progn
     ,@(loop for form in forms collect `(format t "Form: ~a~%" ,form))
     ,@forms))

(macroexpand '(echo (+ 3 4) 
                    (print "foo")
                    (random 10)))
;=> (PROGN
;    (FORMAT T "Form: ~a~%" (+ 3 4))
;    (FORMAT T "Form: ~a~%" (PRINT "foo"))
;    (FORMAT T "Form: ~a~%" (RANDOM 10))
;    (+ 3 4)
;    (PRINT "foo")
;    (RANDOM 10))

मैक्रोज़ के बाहर भी बैककोट का इस्तेमाल किया जा सकता है।

मैक्रोज़ में नाम की गड़बड़ी को रोकने के लिए अद्वितीय प्रतीक

एक मैक्रो के विस्तार को अक्सर उन प्रतीकों का उपयोग करने की आवश्यकता होती है जो उपयोगकर्ता द्वारा तर्क के रूप में पारित नहीं किए गए थे (उदाहरण के लिए स्थानीय चर के नाम)। यह सुनिश्चित करना चाहिए कि ऐसे प्रतीक उस प्रतीक के साथ संघर्ष नहीं कर सकते जो उपयोगकर्ता आसपास के कोड में उपयोग कर रहा है।

यह आमतौर पर GENSYM का उपयोग करके हासिल किया जाता है, जो एक ऐसा फ़ंक्शन है जो एक नया अनइनटर्नड प्रतीक लौटाता है।

खराब

नीचे मैक्रो पर विचार करें। यह एक DOTIMES लूप बनाता है जो शरीर के परिणाम को एक सूची में इकट्ठा करता है, जो अंत में वापस आ जाता है।

(defmacro dotimes+collect ((var count) &body body)
  `(let ((result (list)))
     (dotimes (,var ,count (nreverse result))
       (push (progn ,@body) result))))

(dotimes+collect (i 5)
  (format t "~a~%" i)
  (* i i))
; 0
; 1
; 2
; 3
; 4
;=> (0 1 4 9 16)

ऐसा लगता है कि इस मामले में काम करना है, लेकिन अगर उपयोगकर्ता को एक चर नाम RESULT , जो वे शरीर में उपयोग करते हैं, तो परिणाम शायद यह नहीं होगा कि उपयोगकर्ता क्या अपेक्षा करता है। एक फ़ंक्शन लिखने के इस प्रयास पर विचार करें जो N तक सभी पूर्णांकों की रकमों की सूची एकत्र करता है:

(defun sums-upto (n)
  (let ((result 0))
    (dotimes+collect (i n)
      (incf result i))))

(sums-upto 10) ;=> Error!

अच्छा

समस्या को ठीक करने के लिए, हमें स्थूल विस्तार में RESULT -variable के लिए एक अद्वितीय नाम उत्पन्न करने के लिए GENSYM का उपयोग करने की आवश्यकता है।

(defmacro dotimes+collect ((var count) &body body)
  (let ((result-symbol (gensym "RESULT")))
    `(let ((,result-symbol (list)))
       (dotimes (,var ,count (nreverse ,result-symbol))
         (push (progn ,@body) ,result-symbol)))))

(sums-upto 10) ;=> (0 1 3 6 10 15 21 28 36 45)

TODO: तार से प्रतीकों को कैसे बनाया जाए

TODO: विभिन्न पैकेजों में प्रतीकों के साथ समस्याओं से बचना

अगर-जाने दो, जब-तब, -लेट मैक्रो

ये मैक्रोज़ नियंत्रण प्रवाह और बंधन को मिलाते हैं। वे एनाफॉरिक एनॉफिक मैक्रोज़ पर एक सुधार हैं क्योंकि वे डेवलपर को नामकरण के माध्यम से संवाद करने देते हैं। जैसे कि उनके उपयोग की अनुशंसा उनके अज्ञात समकक्षों से अधिक की जाती है।

(if-let (user (get-user user-id))
  (show-dashboard user)
  (redirect 'login-page))

FOO-LET मैक्रोज़ एक या अधिक चरों को बाँधते हैं, और फिर उन चरों को संगत WHEN ( IF , WHEN ) के लिए परीक्षण रूप में उपयोग करते हैं। एकाधिक चर AND के साथ संयुक्त हैं। चुनी गई शाखा को प्रभावी रूप से बाइंडिंग के साथ निष्पादित किया जाता है। IF-LET का एक सरल एक चर कार्यान्वयन कुछ इस तरह दिखाई दे सकता है:

(defmacro if-let ((var test-form) then-form &optional else-form)
  `(let ((,var ,test-form))
     (if ,var ,then-form ,else-form)))

(macroexpand '(if-let (a (getf '(:a 10 :b 20 :c 30) :a))
               (format t "A: ~a~%" a)
               (format t "Not found.~%")))
; (LET ((A (GETF '(:A 10 :B 20 :C 30) :A)))
;   (IF A
;       (FORMAT T "A: ~a~%" A)
;       (FORMAT T "Not found.~%")))

एक संस्करण जो कई चर का समर्थन करता है वह अलेक्जेंड्रिया पुस्तकालय में उपलब्ध है।

डेटा संरचनाओं को परिभाषित करने के लिए मैक्रोज़ का उपयोग करना

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

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

(defmacro notifier (class slot) 
  "Defines a setf method in (class) for (slot) which calls the object's changed method."
   `(defmethod (setf ,slot) (val (item ,class))
     (setf (slot-value item ',slot) val)
     (changed item ',slot)))

(defmacro notifiers (class slots)
  "Defines setf methods in (class) for all of (slots) which call the object's changed method."
  `(progn 
     ,@(loop for s in slots collecting `(notifier ,class ,s))))

(defmacro defclass-notifier-slots (class nslots slots)  
  "Defines a class with (nslots) giving a list of slots created with notifiers, and (slots) giving a list of slots created with regular accessors."
  `(progn
     (defclass ,class () 
       ( ,@(loop for s in nslots collecting `(,s :reader ,s)) 
         ,@(loop for s in slots collecting `(,s :accessor ,s))))   
     (notifiers ,class ,nslots)))

अब हम लिख सकते हैं (defclass-notifier-slots foo (bar baz qux) (waldo)) और तुरंत एक क्लास foo को रेग्युलर स्लॉट waldo साथ परिभाषित करें (waldo :accessor waldo) विनिर्देश के साथ मैक्रो के दूसरे भाग द्वारा बनाया गया है (defclass-notifier-slots foo (bar baz qux) (waldo)) (waldo :accessor waldo) ) , और स्लॉट bar , baz , और qux setters कि फोन के साथ changed विधि (जहां गेटर मैक्रो, के पहले भाग से परिभाषित किया गया है (bar :reader bar) , और लागू द्वारा सेटर notifier मैक्रो)।

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



Modified text is an extract of the original Stack Overflow Documentation
के तहत लाइसेंस प्राप्त है CC BY-SA 3.0
से संबद्ध नहीं है Stack Overflow