लिस्प मैक्रोज़ क्या इतना खास बनाता है?


297

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

कृपया इसे किसी ऐसी चीज़ से भी संबंधित करें जिसे मैं पायथन, जावा, सी # या सी विकास की दुनिया से समझूंगा।


2
वैसे, C # के लिए एक LISP- शैली मैक्रो प्रोसेसर है, जिसे LeMP: ecsharp.net/lemp ... जावास्क्रिप्ट में एक भी है जिसे Sweet.js: sweetjs.org
क्वर्टी

@Qwertie क्या स्वीटज भी इन दिनों काम करते हैं?
fredrik.hjarner 19

मैंने इसका उपयोग नहीं किया है, लेकिन सबसे हालिया प्रतिबद्ध छह महीने पहले ... मेरे लिए काफी अच्छा था!
Qwertie

जवाबों:


308

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

यहाँ एक और अधिक ठोस उदाहरण है:
पायथन में भाषा में निर्मित सूची समझ है। यह एक सामान्य मामले के लिए एक सरल वाक्यविन्यास देता है। रेखा

divisibleByTwo = [x for x in range(10) if x % 2 == 0]

एक सूची देता है जिसमें 0 और 9 के बीच सभी संख्याएँ होती हैं। पाइथन 1.5 दिनों में वापस ऐसा कोई सिंटैक्स नहीं था; आप इस तरह से कुछ और उपयोग करेंगे:

divisibleByTwo = []
for x in range( 10 ):
   if x % 2 == 0:
      divisibleByTwo.append( x )

ये दोनों कार्यात्मक रूप से समतुल्य हैं। चलो अविश्वास के हमारे निलंबन का आह्वान करते हैं और दिखावा करते हैं कि लिस्प के पास एक बहुत ही सीमित लूप मैक्रो है जो बस पुनरावृत्ति करता है और सूची बोध के बराबर करने का कोई आसान तरीका नहीं है।

लिस्प में आप निम्नलिखित लिख सकते हैं। मुझे यह ध्यान देना चाहिए कि यह आकस्मिक उदाहरण पायथन कोड के समान है जिसे लिस्प कोड का अच्छा उदाहरण नहीं है।

;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
  (if (= x 0)
      (list x)
      (cons x (range-helper (- x 1)))))

(defun range (x)
  (reverse (range-helper (- x 1))))

;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)

;; loop from 0 upto and including 9
(loop for x in (range 10)
   ;; test for divisibility by two
   if (= (mod x 2) 0) 
   ;; append to the list
   do (setq divisibleByTwo (append divisibleByTwo (list x))))

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

बेशक, बहुत सारे टाइपिंग और प्रोग्रामर आलसी हैं। तो हम सूची बोध करने के लिए DSL को परिभाषित कर सकते हैं। वास्तव में, हम पहले से ही एक मैक्रो का उपयोग कर रहे हैं (लूप मैक्रो)।

लिस्प विशेष सिंटैक्स रूपों के एक जोड़े को परिभाषित करता है। उद्धरण ( ') इंगित करता है कि अगला टोकन एक शाब्दिक है। क्यूसिकोट या बैकटिक ( `) इंगित करता है कि अगला टोकन पलायन के साथ एक शाब्दिक है। अल्पविराम ऑपरेटर द्वारा पलायन का संकेत दिया जाता है। शाब्दिक '(1 2 3)है पायथन के बराबर [1, 2, 3]। आप इसे दूसरे वेरिएबल में असाइन कर सकते हैं या इसका इस्तेमाल कर सकते हैं। आप `(1 2 ,x)पायथन के समतुल्य के रूप में सोच सकते हैं [1, 2, x]जहां xपहले से परिभाषित एक चर है। यह सूची नोटेशन मैक्रो में जाने वाले जादू का हिस्सा है। दूसरा भाग लिस्प रीडर है, जो समझदारी से मैक्रोज़ को कोड के लिए स्थानापन्न करता है, लेकिन इसका सबसे अच्छा विवरण नीचे दिया गया है:

इसलिए हम एक मैक्रो को परिभाषित कर सकते हैं lcomp(सूची समझ के लिए छोटा)। यह वाक्य रचना बिल्कुल उस अजगर की तरह होगा जिसका हमने उदाहरण में प्रयोग किया है [x for x in range(10) if x % 2 == 0] -(lcomp x for x in (range 10) if (= (% x 2) 0))

(defmacro lcomp (expression for var in list conditional conditional-test)
  ;; create a unique variable name for the result
  (let ((result (gensym)))
    ;; the arguments are really code so we can substitute them 
    ;; store nil in the unique variable name generated above
    `(let ((,result nil))
       ;; var is a variable name
       ;; list is the list literal we are suppose to iterate over
       (loop for ,var in ,list
            ;; conditional is if or unless
            ;; conditional-test is (= (mod x 2) 0) in our examples
            ,conditional ,conditional-test
            ;; and this is the action from the earlier lisp example
            ;; result = result + [x] in python
            do (setq ,result (append ,result (list ,expression))))
           ;; return the result 
       ,result)))

अब हम कमांड लाइन पर निष्पादित कर सकते हैं:

CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)

बहुत साफ, हुह? अब यह वहाँ बंद नहीं करता है। आपके पास एक तंत्र, या एक तूलिका है, यदि आप चाहें। आपके पास कोई भी वाक्यविन्यास हो सकता है जिसे आप संभवतः चाहते हैं। जैसे पायथन या C # का withसिंटैक्स। या .NET का LINQ सिंटेक्स। अंत में, यह वही है जो लोगों को लिस्प - परम लचीलेपन के लिए आकर्षित करता है।


51
लिस्प में कार्यान्वयन की समझ के लिए +1, क्योंकि क्यों नहीं?
ckb

9
: @ckb असल में लिस्प पहले से ही मानक पुस्तकालय में एक सूची समझ मैक्रो है (loop for x from 0 below 10 when (evenp x) collect x), यहाँ और अधिक उदाहरण । लेकिन वास्तव में, लूप "सिर्फ एक मैक्रो" है (मैं वास्तव में इसे कुछ समय पहले खरोंच से फिर से लागू किया है )
सुज़ैन ड्यूप्रेन

8
मुझे पता है कि यह बहुत असंबंधित है, लेकिन मैं सिंटैक्स के बारे में सोच रहा हूं और वास्तव में पार्सिंग कैसे काम करता है ... कहते हैं कि मैं इस तरह से एलआईसी को कॉल करता हूं ("के लिए" से "azertyuiop" के लिए चोर आइटम बदल रहा है): (lcomp x azeruiuiop x में) रेंज 10) अगर (= (% x 2) 0)) क्या मैक्रो अभी भी उम्मीद के मुताबिक काम करेगा? या लूप में इस्तेमाल होने वाला "फॉर" पैरामीटर है, ताकि इसे कॉल करने के लिए "स्ट्रिंग" होना चाहिए?
दादर

2
@ डेडर तो, लूप वास्तव में एक और मैक्रो है और उस मैक्रो में इस्तेमाल होने वाला एक विशेष प्रतीक है। मैं लूप मैक्रो के बिना मैक्रो को आसानी से परिभाषित कर सकता था। हालांकि, अगर मेरे पास होता, तो पद बहुत लंबा होता। लूप मैक्रो और उसके सभी सिंटैक्स को CLtL में परिभाषित किया गया है
gte525u

3
एक बात जो मैं अन्य भाषा के मैक्रोज़ के बारे में उलझन में हूँ, वह यह है कि उनके मैक्रोज़ होस्ट भाषा के सिंटैक्स द्वारा सीमित हैं। क्या लिस्फी मैक्रों गैर-लिस्फी वाक्य-विन्यास की व्याख्या कर सकते हैं। मेरा मतलब है कि सिंटैक्स (कोई परसेंटेज) की तरह एक हैस्केल बनाने की कल्पना करना और लिस्प मैक्रोज़ का उपयोग करके व्याख्या करना। क्या यह संभव है, और सीधे मैकर और पार्सर का उपयोग करने की तुलना में मैक्रोज़ का उपयोग करने के पक्ष / विपक्ष क्या हैं?
CMCDragonkai

105

आपको यहां लिस्प मैक्रो के आसपास एक व्यापक बहस मिलेगी ।

उस लेख का एक दिलचस्प सबसेट:

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

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

यहाँ एक विस्तारित उदाहरण है। लिस्प में एक मैक्रो है, जिसे "सेटफ" कहा जाता है, जो असाइनमेंट करता है। सेटफ का सबसे सरल रूप है

  (setf x whatever)

जो कि प्रतीक "x" के मान को "जो भी" के मान पर सेट करता है।

लिस्प में भी सूचियां हैं; आप "कार" और "सीडीआर" फ़ंक्शंस का उपयोग किसी सूची के पहले तत्व या बाकी की सूची को प्राप्त करने के लिए कर सकते हैं।

अब क्या होगा अगर आप किसी सूची के पहले तत्व को नए मान से बदलना चाहते हैं? ऐसा करने के लिए एक मानक कार्य है, और अविश्वसनीय रूप से, इसका नाम "कार" से भी बदतर है। यह "rplaca" है। लेकिन आपको "rplaca" याद रखने की ज़रूरत नहीं है, क्योंकि आप लिख सकते हैं

  (setf (car somelist) whatever)

somelist की कार सेट करने के लिए।

यहाँ वास्तव में क्या हो रहा है कि "सेटफ" एक मैक्रो है। संकलन के समय, यह उसके तर्कों की जांच करता है, और यह देखता है कि पहले वाले के पास फॉर्म (कार सोमिंग) है। यह खुद से कहता है "ओह, प्रोग्रामर सोमिंग की कार को सेट करने की कोशिश कर रहा है। इसके लिए उपयोग करने का कार्य 'आरप्लाका' है।" और यह चुपचाप जगह में कोड को फिर से लिखता है:

  (rplaca somelist whatever)

5
सेटफ मैक्रों की ताकत का अच्छा चित्रण है, इसके लिए धन्यवाद।
जोएल

मुझे हाइलाइट पसंद है .. लेकिन लिस्प प्रोग्राम का स्रोत स्ट्रिंग नहीं है; यह एक सूची है। ! क्या यह मुख्य कारण है कि LISP मैक्रो अपने कोष्ठक के कारण दूसरों की तुलना में बेहतर है?
छात्र

@ स्टूडेंट मुझे लगता है कि: books.google.fr/… आपको पता चलता है कि आप सही हैं।
VonC

55

आम लिस्प मैक्रोज़ अनिवार्य रूप से आपके कोड के "सिंटैक्टिक प्राइमेटिव्स" का विस्तार करते हैं।

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

लेकिन, चूंकि एक लिस्प मैक्रो (अनिवार्य रूप से) एक लिस्प प्रोग्राम है जो इनपुट के रूप में कोड के स्निपेट लेता है और मैक्रो के "इनवोकेशन" को बदलने के लिए कोड देता है, आप अपने "आदिम" प्रदर्शनों की सूची का विस्तार कर सकते हैं जहाँ तक आप चाहते हैं, आमतौर पर समाप्त हो रहा है। अधिक पठनीय कार्यक्रम के साथ।

C में समान करने के लिए, आपको एक कस्टम प्री-प्रोसेसर लिखना होगा जो आपके प्रारंभिक (नहीं-काफी-सी) स्रोत को खाता है और कुछ ऐसा खर्च करता है जिसे C कंपाइलर समझ सकता है। यह इसके बारे में जाने का गलत तरीका नहीं है, लेकिन जरूरी नहीं कि यह सबसे आसान हो।


41

लिस्प मैक्रोज़ आपको यह तय करने की अनुमति देते हैं कि (यदि हो तो) किसी भी हिस्से या अभिव्यक्ति का मूल्यांकन किया जाएगा। एक सरल उदाहरण रखने के लिए, C के बारे में सोचें:

expr1 && expr2 && expr3 ...

यह क्या कहता है: मूल्यांकन expr1, और, क्या यह सच होना चाहिए, मूल्यांकन expr2, आदि।

अब &&इसे एक फंक्शन में बनाने की कोशिश करें ... यह सही है, आप नहीं कर सकते। कॉलिंग कुछ इस तरह है:

and(expr1, expr2, expr3)

exprsएक जवाब देने से पहले तीनों का मूल्यांकन करेंगे चाहे expr1वह गलत हो!

लिस्प मैक्रोज़ के साथ आप कुछ इस तरह से कोड कर सकते हैं:

(defmacro && (expr1 &rest exprs)
    `(if ,expr1                     ;` Warning: I have not tested
         (&& ,@exprs)               ;   this and might be wrong!
         nil))

अब आपके पास एक है &&, जिसे आप केवल एक फ़ंक्शन की तरह कॉल कर सकते हैं और यह आपके द्वारा पास किए गए किसी भी रूप का मूल्यांकन नहीं करेगा जब तक कि वे सभी सच न हों।

यह देखने के लिए कि यह कैसे उपयोगी है, इसके विपरीत:

(&& (very-cheap-operation)
    (very-expensive-operation)
    (operation-with-serious-side-effects))

तथा:

and(very_cheap_operation(),
    very_expensive_operation(),
    operation_with_serious_side_effects());

अन्य चीजें जो आप मैक्रोज़ के साथ कर सकते हैं वे नए कीवर्ड और / या मिनी-भाषाएँ बना रहे हैं ( (loop ...)उदाहरण के लिए मैक्रो देखें), अन्य भाषाओं को लिस्प में एकीकृत करते हुए, उदाहरण के लिए, आप एक मैक्रो लिख सकते हैं जिससे आप कुछ ऐसा कह सकते हैं:

(setvar *rows* (sql select count(*)
                      from some-table
                     where column1 = "Yes"
                       and column2 like "some%string%")

और वह भी रीडर मैक्रोज़ में नहीं मिल रहा है ।

उम्मीद है की यह मदद करेगा।


मुझे लगता है कि यह होना चाहिए: "(लागू &&, @ exprs), यह गलत हो सकता है!"
Svante

1
@svante - दो गणनाओं पर: पहला, && एक मैक्रो है, एक फ़ंक्शन नहीं; केवल कार्यों पर लागू होता है। दूसरा, लागू करने के लिए तर्कों की एक सूची लें, ताकि आप "(funcall fn, @ exprs)", "(fn (सूची, @ एक्सपर्स) लागू करें" या "(fn, @ exprs nil") लागू करें, नहीं "(एफएन लागू करें, @ एक्सपर्स)"
हारून

(and ...जब तक कोई गलत का मूल्यांकन नहीं करता है, तब तक भावों का मूल्यांकन करेगा, ध्यान दें कि गलत मूल्यांकन से उत्पन्न दुष्प्रभाव होंगे, केवल बाद के भावों को छोड़ दिया जाएगा।
ओसोडो

31

मुझे नहीं लगता कि मैंने कभी लिस्प मैक्रोज़ को इस साथी से बेहतर समझा है: http://www.defmacro.org/ramblings@lb.html


3
खासकर यदि आपके पास जावा / एक्सएमएल पृष्ठभूमि है।
सूर्यास्त

1
शनिवार की दोपहर मेरे सोफे पर लेटे हुए इसे पढ़ने का क्या आनंद! बहुत स्पष्ट रूप से लिखा और संगठित।
कॉलिन

10

आप मैक्रो और टेम्प्लेट के साथ C या C ++ में क्या कर सकते हैं, इसके बारे में सोचें। वे दोहराव कोड को प्रबंधित करने के लिए बहुत उपयोगी उपकरण हैं, लेकिन वे काफी गंभीर तरीकों से सीमित हैं।

  • सीमित मैक्रो / टेम्पलेट सिंटैक्स उनके उपयोग को प्रतिबंधित करता है। उदाहरण के लिए, आप एक खाका नहीं लिख सकते हैं जो किसी वर्ग या फ़ंक्शन के अलावा किसी अन्य चीज़ के लिए विस्तारित हो। मैक्रोज़ और टेम्प्लेट आसानी से आंतरिक डेटा को बनाए नहीं रख सकते हैं।
  • सी और सी ++ का जटिल, बहुत अनियमित सिंटैक्स बहुत सामान्य मैक्रोज़ लिखना मुश्किल बनाता है।

लिस्प और लिस्प मैक्रोज़ इन समस्याओं को हल करते हैं।

  • लिस्प में मैक्रों लिखे जाते हैं। आपके पास मैक्रो लिखने के लिए लिस्प की पूरी शक्ति है।
  • लिस्प में एक बहुत ही नियमित वाक्यविन्यास है।

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


11
ठीक है, निष्पक्ष होने के लिए, सी ++ टेम्पलेट मेटाप्रोग्रामिंग के साथ समस्या यह नहीं है कि मेटाप्रोग्रामिंग भाषा अलग है , लेकिन यह डरावनी है - यह इतना डिज़ाइन नहीं किया गया था जितना कि बहुत सरल-सरल टेम्पलेट कार्यक्षमता का इरादा था।
ब्रूक्स मूसा

@ बरोक ज़रूर। इमर्जेंट फीचर्स हमेशा खराब नहीं होते हैं। दुर्भाग्य से, एक धीमी गति से चलती समिति संचालित भाषा में, जब वे होते हैं तो उन्हें ठीक करना मुश्किल होता है। यह शर्म की बात है कि C ++ के आधुनिक उपयोगी नए फीचर्स बहुत कुछ एक भाषा में लिखे गए हैं जिन्हें कुछ भी पढ़ा जा सकता है, और औसत प्रोग्रामर और "उच्च पुजारी" के बीच बहुत बड़ा अंतर है।
मैट कर्टिस

2
@downvoter: यदि मेरे उत्तर में कुछ गड़बड़ है, तो कृपया एक टिप्पणी छोड़ें ताकि हम सभी ज्ञान साझा कर सकें।
मैट कर्टिस

10

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

C # में मैक्रो की सुविधा नहीं है, हालांकि एक समतुल्य होगा यदि कंपाइलर ने कोडडॉम-ट्री में कोड डाला, और उसे एक विधि में पारित कर दिया, जिसने इसे दूसरे कोडडॉम में बदल दिया, जिसे तब IL में संकलित किया गया।

यह लागू करने के लिए की तरह "चीनी" वाक्यविन्यास इस्तेमाल किया जा सकता for each-statement using-clause, LINQ select, -expressions और इतने पर मैक्रो के रूप में अंतर्निहित कोड में परिवर्तित हो कि।

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

यहाँ छद्म कोड है जिसको लागू करने के लिए C # में एक लिस्प-शैली मैक्रो कैसे usingदेख सकते हैं:

define macro "using":
    using ($type $varname = $expression) $block
into:
    $type $varname;
    try {
       $varname = $expression;
       $block;
    } finally {
       $varname.Dispose();
    }

अब जब कि वहाँ वास्तव में है सी # के लिए एक लिस्प शैली मैक्रो प्रोसेसर, मैं कहना है कि के लिए एक मैक्रो usingहैं इस तरह दिखेगा ;)
Qwertie

9

चूंकि मौजूदा जवाब अच्छे ठोस उदाहरण देते हैं, जो बताते हैं कि मैक्रोज़ क्या हासिल करते हैं और कैसे, शायद यह कुछ विचारों को एक साथ इकट्ठा करने में मदद करेगा कि क्यों मैक्रो सुविधा अन्य भाषाओं के संबंध में एक महत्वपूर्ण लाभ है ; पहले इन उत्तरों से, फिर एक महान अन्यत्र से:

... सी में, आपको एक कस्टम प्री-प्रोसेसर लिखना होगा [जो कि संभवतः एक जटिल सी प्रोग्राम के रूप में योग्य होगा ] ...

- वैटीन

सी ++ में महारत हासिल करने वाले किसी से भी बात करें और उनसे पूछें कि उन्होंने सभी टेम्प्लेट फ्यूडरी सीखने में कितना समय लगाया है, उन्हें टेम्प्लेट मेटाप्रोग्रामिंग [जो अभी भी उतना शक्तिशाली नहीं है] करने की आवश्यकता है।

- मैट कर्टिस

... जावा में आपको बाईटकोड बुनाई के साथ अपना रास्ता हैक करना होगा, हालांकि एस्पेक्टज जैसे कुछ ढांचे आपको एक अलग दृष्टिकोण का उपयोग करके ऐसा करने की अनुमति देते हैं, यह मौलिक रूप से एक हैक है।

- मिगुएल पिंग

DOLIST, पर्ल के फॉर्च या पाइथन के लिए समान है। Java ने JS 1.5 के भाग के रूप में जावा 1.5 में लूप के लिए "संवर्धित" के साथ एक समान प्रकार का लूप निर्माण जोड़ा। ध्यान दें कि मैक्रो क्या अंतर करता है। एक लिस्प प्रोग्रामर जो अपने कोड में एक सामान्य पैटर्न को नोटिस करता है, अपने आप को उस पैटर्न के स्रोत-स्तरीय अमूर्तता देने के लिए एक मैक्रो लिख सकता है। एक जावा प्रोग्रामर, जो एक ही पैटर्न को नोटिस करता है, उसे सूर्य को समझाना पड़ता है कि यह विशेष अमूर्त भाषा में जोड़ने लायक है। फिर रवि को एक JSR प्रकाशित करना है और सब कुछ हैश करने के लिए एक उद्योग-व्यापी "विशेषज्ञ समूह" को बुलाना है। यह प्रक्रिया - सूर्य के अनुसार - औसतन 18 महीने लगती है। उसके बाद, संकलक लेखकों को नई सुविधा का समर्थन करने के लिए अपने संकलक को अपग्रेड करना होगा। और यहां तक ​​कि एक बार जावा प्रोग्रामर का पसंदीदा कंपाइलर जावा के नए संस्करण का समर्थन करता है, जब तक वे जावा के पुराने संस्करणों के साथ स्रोत संगतता को तोड़ने की अनुमति नहीं देते, तब तक वे शायद '' अभी भी '' नई सुविधा का उपयोग नहीं कर सकते। तो एक नाराज़गी कि कॉमन लिस्प प्रोग्रामर अपने लिए पांच मिनट के भीतर हल कर सकते हैं, सालों के लिए जावा प्रोग्रामर्स को परेशान करते हैं।

- पीटर सीबेल, "प्रैक्टिकल कॉमन लिस्प" में


8

मुझे यकीन नहीं है कि मैं सभी के (उत्कृष्ट) पोस्टों में कुछ अंतर्दृष्टि जोड़ सकता हूं, लेकिन ...

लिस्प सिंटैक्स प्रकृति के कारण लिस्प मैक्रोज़ महान काम करते हैं।

लिस्प एक अत्यंत नियमित भाषा है (सब कुछ एक सूची है ); मैक्रोज़ आपको डेटा और कोड के समान व्यवहार करने में सक्षम बनाता है (लिस्प अभिव्यक्तियों को संशोधित करने के लिए कोई स्ट्रिंग पार्सिंग या अन्य हैक की आवश्यकता नहीं होती है)। आप इन दो सुविधाओं गठबंधन और आप एक बहुत है स्वच्छ कोड को संशोधित करने के लिए रास्ता।

संपादित करें: मैं जो कहना चाहता था, वह यह है कि लिस्प होम्योनिक है , जिसका अर्थ है कि लिस्प प्रोग्राम के लिए डेटा संरचना लिस्प में ही लिखी जाती है।

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

व्यवहार में, मैक्रोज़ के साथ आप लिस्प के शीर्ष पर अपनी खुद की मिनी-भाषा का निर्माण करते हैं , अतिरिक्त भाषाओं या टूलींग सीखने की आवश्यकता के बिना, और भाषा की पूरी शक्ति का उपयोग करने के साथ।


1
हालांकि, यह व्यावहारिक टिप्पणी है, लेकिन यह विचार "सब कुछ एक सूची है" नए लोगों को डरा सकता है। एक सूची को समझने के लिए, आपको विपक्ष, कारों, सीडीआर, कोशिकाओं को समझने की आवश्यकता है। अधिक सटीक रूप से, लिस्प से बना है S-expressions, सूचियों से नहीं।
रिबमार

6

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

अजगर में वस्तुओं की दो विधियाँ होती हैं __repr__और __str____str__बस मानव पठनीय प्रतिनिधित्व है। __repr__एक प्रतिनिधित्व देता है जो वैध पायथन कोड है, जो कहना है, कुछ ऐसा है जिसे दुभाषिया में वैध पायथन के रूप में दर्ज किया जा सकता है। इस तरह से आप पायथन के छोटे स्निपेट बना सकते हैं जो वैध कोड उत्पन्न करते हैं जिन्हें आपके वास्तव में स्रोत में चिपकाया जा सकता है।

लिस्प में इस पूरी प्रक्रिया को मैक्रो सिस्टम द्वारा औपचारिक रूप दिया गया है। यकीन है कि यह आपको सिंटैक्स में एक्सटेंशन बनाने और सभी प्रकार की फैंसी चीजें करने में सक्षम बनाता है, लेकिन यह वास्तविक उपयोगिता है जो ऊपर से अभिव्यक्त किया गया है। बेशक यह मदद करता है कि लिस्प मैक्रो सिस्टम आपको इन "स्निपेट्स" को पूरी भाषा की पूरी शक्ति के साथ हेरफेर करने की अनुमति देता है।


1
आपका पहला पैराग्राफ एक लिस्प बाहरी व्यक्ति के लिए बहुत स्पष्ट है, जो महत्वपूर्ण है।
वाइल्डकार्ड

5

संक्षेप में, मैक्रोज़ कोड के रूपांतरण हैं। वे कई नए वाक्यविन्यास निर्माण शुरू करने की अनुमति देते हैं। जैसे, LINQ को C # में मानते हैं। लिस्प में, समान भाषा एक्सटेंशन हैं जो मैक्रोज़ द्वारा कार्यान्वित किए जाते हैं (उदाहरण के लिए, अंतर्निहित लूप निर्माण, पुनरावृति)। मैक्रोज़ ने कोड दोहराव को काफी कम कर दिया। मैक्रोज़ «छोटी भाषाओं» को एम्बेड करने की अनुमति देते हैं (उदाहरण के लिए, जहां सी # / जावा में एक कॉन्फ़िगर करने के लिए xml का उपयोग करेगा, लिस्प में मैक्रो के साथ एक ही चीज हासिल की जा सकती है)। मैक्रों पुस्तकालयों के उपयोग की कठिनाइयों को छिपा सकते हैं।

जैसे, लिस्प में आप लिख सकते हैं

(iter (for (id name) in-clsql-query "select id, name from users" on-database *users-database*)
      (format t "User with ID of ~A has name ~A.~%" id name))

और यह सभी डेटाबेस सामान (लेन-देन, उचित कनेक्शन बंद करना, डेटा प्राप्त करना आदि) को छुपाता है जबकि C # में इसके लिए SqlConnections, SqlCommands बनाने की आवश्यकता होती है, SqlParameters को SqlCadands में जोड़ना, SqlDataReaders पर लूप करना, उन्हें ठीक से बंद करना।


3

जबकि उपरोक्त सभी बताते हैं कि मैक्रोज़ क्या हैं और यहां तक ​​कि शांत उदाहरण भी हैं, मुझे लगता है कि मैक्रो और सामान्य फ़ंक्शन के बीच महत्वपूर्ण अंतर यह है कि LISP फ़ंक्शन को कॉल करने से पहले सभी मापदंडों का मूल्यांकन करता है। एक मैक्रो के साथ यह रिवर्स है, एलआईएसपी मैक्रो के लिए अपरिवर्तित मापदंडों को पास करता है। उदाहरण के लिए, यदि आप किसी फ़ंक्शन को (+ 1 2) पास करते हैं, तो फ़ंक्शन को मान 3. प्राप्त होगा। यदि आप इसे किसी मैक्रो को पास करते हैं, तो यह एक सूची (+ 1 2) प्राप्त करेगा। इसका उपयोग सभी प्रकार के अविश्वसनीय रूप से उपयोगी सामान करने के लिए किया जा सकता है।

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

    (defmacro working-timer (b) 
      (let (
            (start (get-universal-time))
            (result (eval b))) ;; not splicing here to keep stuff simple
        ((- (get-universal-time) start))))
    
    (defun my-broken-timer (b)
      (let (
            (start (get-universal-time))
            (result (eval b)))    ;; doesn't even need eval
        ((- (get-universal-time) start))))
    
    (working-timer (sleep 10)) => 10
    
    (broken-timer (sleep 10)) => 0

Btw, स्काला ने मैक्रोज़ को भाषा में जोड़ा है। हालांकि उनके पास लिस्प मैक्रो की सुंदरता की कमी है क्योंकि भाषा होमोसेक्सुअल नहीं है, वे निश्चित रूप से इसके लायक हैं और सार सिंटैक्स पेड़ों को वे प्रदाता को अंत में उपयोग करना आसान हो सकता है। मेरे लिए यह कहना जल्दबाजी होगी कि मुझे कौन सी मैक्रो प्रणाली पसंद है।
जोर्ग श्मुकर

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

0

मुझे यह आम लिस्प रसोई की किताब से मिला, लेकिन मुझे लगता है कि यह बताया गया है कि लिस्प मैक्रोज़ एक अच्छे तरीके से अच्छे क्यों हैं।

"एक मैक्रो लिस्प कोड का एक साधारण टुकड़ा है, जो इसे डालने योग्य लिस्प के एक और टुकड़े में लिपटे हुए लिस्प कोड के एक अन्य टुकड़े पर संचालित होता है। यह थोड़ा जटिल लग सकता है। इसलिए एक सरल उदाहरण दें। मान लीजिए कि आप चाहते हैं। setq का संस्करण जो एक ही मान के दो चर सेट करता है। यदि आप लिखते हैं

(setq2 x y (+ z 3))

जब z=8x और y दोनों 11 पर सेट होते हैं (मैं इसके लिए किसी भी उपयोग के बारे में नहीं सोच सकता, लेकिन यह सिर्फ एक उदाहरण है।)

यह स्पष्ट होना चाहिए कि हम setq2 को एक फ़ंक्शन के रूप में परिभाषित नहीं कर सकते हैं। तो x=50और y=-5, इस समारोह मूल्यों प्राप्त होगा 50, -5, और 11; इसके बारे में कोई जानकारी नहीं होगी कि किस चर को सेट किया जाना चाहिए था। हम वास्तव में क्या कहना चाहते हैं, जब आप (लिस्प प्रणाली) देखते हैं (setq2 v1 v2 e), तो इसे समकक्ष मानें (progn (setq v1 e) (setq v2 e))। वास्तव में, यह काफी सही नहीं है, लेकिन यह अभी के लिए क्या करेगा। एक मैक्रो हमें इनपुट पैटर्न को (setq2 v1 v2 e)"आउटपुट पैटर्न में बदलने " के लिए एक प्रोग्राम निर्दिष्ट करके, ठीक से ऐसा करने की अनुमति देता है (progn ...)

अगर आपको लगता है कि यह अच्छा था तो आप यहाँ पढ़ सकते हैं: http://cl-cookbook.sourceforge.net/macros.html


1
यह setq2एक फ़ंक्शन के रूप में परिभाषित करना संभव है यदि xऔर yसंदर्भ द्वारा पारित किया जाता है। मुझे नहीं पता कि क्या यह सीएल में संभव है, हालांकि। तो ऐसे किसी व्यक्ति के लिए जो लिसप्स या सीएल को विशेष रूप से नहीं जानता है, यह बहुत
निराशाजनक

@neoascetic CL तर्क पासिंग वैल्यू से ही है (इसीलिए इसे पहली जगह में मैक्रोज़ की जरूरत है)। कुछ मान हालांकि (सूचियों की तरह) संकेत हैं।
विल नेस

-5

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.