एलआईएसपी के बारे में क्या, अगर कुछ भी, तो मैक्रो सिस्टम को लागू करना आसान हो जाता है?


21

मैं SICP से स्कीम सीख रहा हूं और मुझे आभास हो रहा है कि स्कीम का एक बड़ा हिस्सा और इससे भी ज्यादा, LISP विशेष मैक्रो सिस्टम है। लेकिन, चूंकि मैक्रोज़ का संकलन-समय पर विस्तार किया गया है, इसलिए लोग C / Python / Java / जो भी हो, के लिए समतुल्य स्थूल सिस्टम नहीं बनाते हैं? उदाहरण के लिए, कोई भी या जो भी pythonआदेश को बाँध सकता है expand-macros | python। यह कोड अभी भी उन लोगों के लिए पोर्टेबल होगा जो मैक्रो सिस्टम का उपयोग नहीं करते हैं, एक बस कोड को प्रकाशित करने से पहले मैक्रोज़ का विस्तार करेगा। लेकिन मुझे सी ++ / हास्केल में टेम्पलेट को छोड़कर ऐसा कुछ भी नहीं पता है, जो मैं इकट्ठा करता हूं, वास्तव में ऐसा नहीं है। एलआईएसपी के बारे में क्या, अगर कुछ भी, तो मैक्रो सिस्टम को लागू करना आसान हो जाता है?


3
"कोड अभी भी उन लोगों के लिए पोर्टेबल होगा जो मैक्रो सिस्टम का उपयोग नहीं करते हैं, एक बस कोड को प्रकाशित करने से पहले मैक्रोज़ का विस्तार करेगा।" - बस आपको चेतावनी देने के लिए, यह अच्छी तरह से काम नहीं करता है। वे अन्य लोग कोड को चलाने में सक्षम होंगे, लेकिन व्यवहार में मैक्रो-विस्तारित कोड को समझना मुश्किल है, और आमतौर पर संशोधित करना मुश्किल है। यह इस अर्थ में "बुरी तरह से लिखा" है कि लेखक ने मानवीय आंखों के लिए विस्तारित कोड को नहीं बनाया है, उन्होंने वास्तविक स्रोत को सिलवाया है। एक जावा प्रोग्रामर को यह बताने की कोशिश करें कि आप सी प्रीप्रोसेसर के माध्यम से अपना जावा कोड चलाते हैं और देखते हैं कि वे किस रंग में बदल जाते हैं ;-)
स्टीव जेसोप

1
मैक्रों को हालांकि निष्पादित करने की आवश्यकता है, उस बिंदु पर आप पहले से ही भाषा के लिए एक दुभाषिया लिख ​​रहे हैं।
मेहरदाद

जवाबों:


29

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

(define (hypot x y)
  (sqrt (+ (square x) (square y))))

अब, समरूपता का कहना है कि लिस्प कोड में डेटा कोड (विशेष रूप से, सूचियों की सूची) के रूप में उपरोक्त कोड वास्तव में प्रतिनिधित्व करने योग्य है। इस प्रकार, निम्नलिखित सूचियों पर विचार करें और देखें कि वे एक साथ "गोंद" कैसे करते हैं:

  1. (define #2# #3#)
  2. (hypot x y)
  3. (sqrt #4#)
  4. (+ #5# #6#)
  5. (square x)
  6. (square y)

मैक्रोज़ आपको स्रोत कोड का इलाज करने की अनुमति देते हैं: सामान की सूची। उन 6 "उप-सूचियों" के प्रत्येक अन्य सूचियों के लिए या तो संकेत दिए गए होते हैं, या प्रतीकों के लिए (इस उदाहरण में: define, hypot, x, y, sqrt, +, square)।


तो, हम समरूपता को "अलग" करने और मैक्रो बनाने के लिए समरूपता का उपयोग कैसे कर सकते हैं? यहाँ एक सरल उदाहरण है। आइए letमैक्रो को फिर से लागू करें , जिसे हम कॉल करेंगे my-let। अनुस्मारक के रूप में,

(my-let ((foo 1)
         (bar 2))
  (+ foo bar))

में विस्तार होना चाहिए

((lambda (foo bar)
   (+ foo bar))
 1 2)

यहाँ योजना "स्पष्ट नाम" मैक्रो का उपयोग कर एक कार्यान्वयन है :

(define-syntax my-let
  (er-macro-transformer
    (lambda (form rename compare)
      (define bindings (cadr form))
      (define body (cddr form))
      `((,(rename 'lambda) ,(map car bindings)
          ,@body)
        ,@(map cadr bindings)))))

formपैरामीटर वास्तविक रूप के लिए बाध्य है, इसलिए हमारे उदाहरण के लिए, यह होगा (my-let ((foo 1) (bar 2)) (+ foo bar))। तो, चलो उदाहरण के माध्यम से काम करते हैं:

  1. सबसे पहले, हम फॉर्म से बाइंडिंग को पुनः प्राप्त करते हैं। फॉर्म cadrके ((foo 1) (bar 2))हिस्से को पकड़ लेता है ।
  2. फिर, हम प्रपत्र से शरीर को पुनः प्राप्त करते हैं। फॉर्म cddrके ((+ foo bar))हिस्से को पकड़ लेता है । (ध्यान दें कि यह बंधन के बाद सभी उपग्रहों को हथियाने का इरादा है; इसलिए यदि फॉर्म था

    (my-let ((foo 1)
             (bar 2))
      (debug foo)
      (debug bar)
      (+ foo bar))
    

    तब शरीर होगा ((debug foo) (debug bar) (+ foo bar))।)

  3. अब, हम वास्तव में परिणामी lambdaअभिव्यक्ति का निर्माण करते हैं और हमारे द्वारा एकत्र किए गए बाइंडिंग और शरीर का उपयोग करके कॉल करते हैं। बैकटिक को "क्वासिकोट" कहा जाता है, जिसका अर्थ है कॉमासिक के अंदर की सभी चीज़ों को शाब्दिक डेटा के रूप में व्यवहार करना, कॉमा के बाद बिट्स ("अनक्वॉट") को छोड़कर
    • (rename 'lambda)साधनों का उपयोग करने के लिए lambdaजब इस मैक्रो है बल में बाध्यकारी परिभाषित है, बल्कि जो कुछ भी की तुलना में lambdaबाध्यकारी के आसपास हो सकता है जब इस मैक्रो है इस्तेमाल किया । (इसे स्वच्छता के रूप में जाना जाता है ।)
    • (map car bindings)रिटर्न (foo bar): प्रत्येक बाइंडिंग में पहला डेटम।
    • (map cadr bindings)रिटर्न (1 2): प्रत्येक बाइंडिंग में दूसरा डेटम।
    • ,@ "splicing" करता है, जिसका उपयोग उन अभिव्यक्तियों के लिए किया जाता है जो किसी सूची को लौटाते हैं: यह सूची के तत्वों को परिणाम में चिपकाया जाता है, बजाय सूची के।
  4. उस सब को एक साथ रखते हुए, हम प्राप्त करते हैं, परिणामस्वरूप, सूची (($lambda (foo bar) (+ foo bar)) 1 2), जहां $lambdaयहां नाम बदला गया है lambda

सीधा, ठीक? ;-) (यदि यह आपके लिए सीधा नहीं है, तो बस कल्पना करें कि अन्य भाषाओं के लिए एक मैक्रो सिस्टम को लागू करना कितना मुश्किल होगा)।


तो, आपके पास अन्य भाषाओं के लिए मैक्रो सिस्टम हो सकते हैं, यदि आपके पास एक गैर-क्लंकी तरीके से "कोड को अलग" करने में सक्षम होने का एक तरीका है। इस पर कुछ प्रयास हैं। उदाहरण के लिए, स्वीट .js यह जावास्क्रिप्ट के लिए करता है।

† यह पढ़ने वाले अनुभवी योजनाकारों के लिए, मैंने जानबूझकर स्पष्ट नाम बदलने वाले मैक्रोज़ को defmacroअन्य लिस्प बोलियों द्वारा उपयोग किए जाने वाले मध्य समझौता के रूप में उपयोग करने के लिए चुना , और syntax-rules(जो स्कीम में इस तरह के मैक्रो को लागू करने का मानक तरीका होगा)। मैं अन्य लिस्प बोलियों में नहीं लिखना चाहता, लेकिन मैं उन गैर-स्कीमों को अलग नहीं करना चाहता, जिनका उपयोग नहीं किया जाता है syntax-rules

संदर्भ के लिए, यहां my-letमैक्रो का उपयोग किया गया है syntax-rules:

(define-syntax my-let
  (syntax-rules ()
    ((my-let ((id val) ...)
       body ...)
     ((lambda (id ...)
        body ...)
      val ...))))

संबंधित syntax-caseसंस्करण बहुत समान दिखता है:

(define-syntax my-let
  (lambda (stx)
    (syntax-case stx ()
      ((_ ((id val) ...)
         body ...)
       #'((lambda (id ...)
            body ...)
          val ...)))))

दोनों के बीच अंतर यह है कि हर चीज में syntax-rulesएक निहित #'है, इसलिए आपके पास केवल पैटर्न / टेम्पलेट जोड़े हो सकते हैं syntax-rules, इसलिए यह पूरी तरह से घोषित है। इसके विपरीत, में syntax-case, पैटर्न वास्तविक कोड के बाद का है, अंत में, एक वाक्यविन्यास ऑब्जेक्ट ( #'(...)) वापस करना होगा, लेकिन इसमें अन्य कोड भी हो सकते हैं।


2
एक लाभ जिसका आपने उल्लेख नहीं किया है: हाँ, अन्य भाषाओं में प्रयास हैं, जैसे कि JS के लिए sweet.js। हालाँकि, lisps में, मैक्रो लिखने का कार्य उसी भाषा में किया जाता है, जो फ़ंक्शन लिखता है।
फ्लोरियन मार्गाइन

ठीक है, आप लिस्प भाषाओं में प्रक्रियात्मक (बनाम घोषित) मैक्रोज़ लिख सकते हैं, जो कि आपको वास्तव में उन्नत सामान करने की अनुमति देता है। BTW, यह वही है जो मुझे योजना मैक्रो सिस्टम के बारे में पसंद है: चुनने के लिए कई हैं। सरल मैक्रोज़ के लिए, मैं उपयोग करता हूं syntax-rules, जो विशुद्ध रूप से घोषणात्मक है। जटिल मैक्रोज़ के लिए, मैं उपयोग कर सकता हूं syntax-case, जो आंशिक रूप से घोषणात्मक और आंशिक रूप से प्रक्रियात्मक है। और फिर वहाँ स्पष्ट नामकरण है, जो पूरी तरह से प्रक्रियात्मक है। (अधिकांश योजना कार्यान्वयन syntax-caseया तो प्रदान करेंगे या ईआर। मैंने ऐसा एक नहीं देखा है जो दोनों प्रदान करता है। वे सत्ता में बराबर हैं।)
क्रिस जस्टर-यंग

मैक्रो को एएसटी को संशोधित करने की आवश्यकता क्यों है? वे उच्च स्तर पर काम क्यों नहीं कर सकते?
इलियट गोरोखोवस्की

1
तो फिर LISP बेहतर क्यों है? क्या खास बनाता है LISP? यदि कोई मैक्रोज़ को js में लागू कर सकता है, तो निश्चित रूप से उन्हें किसी अन्य भाषा में भी लागू कर सकता है।
इलियट गोरोखोवस्की

3
@ RenéG जैसा कि मैंने अपनी पहली टिप्पणी में कहा, एक बड़ा फायदा यह है कि आप अभी भी उसी भाषा में लिख रहे हैं।
फ्लोरियन मार्गाइन

23

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

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

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

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

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

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

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

यहाँ एक मैक्रो है जिसे मैंने कल बनाया था जब मुझे एहसास हुआ कि मैं एक ही पैटर्न को GUI कोड में विभिन्न स्थानों के एक समूह में लागू कर रहा हूं, जहां मैं BeginUpdate()UI नियंत्रण पर कॉल करूंगा , एक tryब्लॉक में एक अपडेट करूंगा , और फिर कॉल करूंगा EndUpdate():

macro UIUpdate(value as Expression):
    return [|
        $value.BeginUpdate()
        try:
            $(UIUpdate.Body)
        ensure:
            $value.EndUpdate()
    |]

macroआदेश, वास्तव में, है एक मैक्रो ही , एक है कि इनपुट के रूप में एक मैक्रो शरीर लेता है और मैक्रो कार्रवाई करने के लिए एक वर्ग उत्पन्न करता है। यह मैक्रो के नाम का उपयोग एक चर के रूप में करता है जो MacroStatementएएसटी नोड के लिए खड़ा है जो मैक्रो इनवोकेशन का प्रतिनिधित्व करता है। द [| ... |] एक quasiquote ब्लॉक है, जो एएसटी उत्पन्न करता है जो कोड के भीतर और कोडिकोट ब्लॉक के अंदर स्थित है, $ प्रतीक "unquote" सुविधा प्रदान करता है, जो नोड में निर्दिष्ट है।

इसके साथ, यह लिखना संभव है:

UIUpdate myComboBox:
   LoadDataInto(myComboBox)
   myComboBox.SelectedIndex = 0

और इसका विस्तार करें:

myComboBox.BeginUpdate()
try:
   LoadDataInto(myComboBox)
   myComboBox.SelectedIndex = 0
ensure:
   myComboBox.EndUpdate()

मैक्रो को व्यक्त करना इस तरह से सरल और अधिक सहज है क्योंकि यह एक लिस्प मैक्रो में होगा, क्योंकि डेवलपर की संरचना को MacroStatementजानता है और जानता है कि कैसे Argumentsऔर Bodyगुण काम करते हैं, और उस अंतर्निहित ज्ञान का उपयोग बहुत ही सहज ज्ञान युक्त अवधारणाओं को व्यक्त करने के लिए किया जा सकता है। मार्ग। यह अधिक सुरक्षित भी है, क्योंकि कंपाइलर की संरचना को जानता है MacroStatement, और यदि आप किसी ऐसी चीज़ को कोड करने की कोशिश करते हैं जो ए के लिए मान्य नहीं है MacroStatement, तो कंपाइलर इसे तुरंत पकड़ लेगा और त्रुटि की रिपोर्ट करेगा जब तक कि आपको पता न चले कि आपके ऊपर कुछ नहीं चल रहा है क्रम।

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


4
पढ़ने के लिए खुशी, धन्यवाद! गैर-लिस्प मैक्रो खिंचाव को सिंटैक्स बदलने के रूप में दूर करता है? क्योंकि लिस्प की ताकत में से एक वाक्य रचना सभी समान है, इस प्रकार एक फ़ंक्शन, सशर्त विवरण जोड़ना आसान है, जो कुछ भी क्योंकि वे सभी समान हैं। जबकि गैर-लिस्प भाषाओं के साथ एक चीज दूसरे से भिन्न होती है - if...उदाहरण के लिए फ़ंक्शन कॉल की तरह नहीं दिखती है। मुझे बू का पता नहीं है, लेकिन कल्पना कीजिए कि बू में पैटर्न मिलान नहीं था, क्या आप इसे अपने स्वयं के सिंटैक्स के साथ मैक्रो के रूप में पेश कर सकते हैं? मेरा कहना है - लिस्प में किसी भी नए मैक्रो को 100% प्राकृतिक लगता है, अन्य भाषाओं में वे काम करते हैं, लेकिन आप टाँके देख सकते हैं।
ग्रीनल्डमैन

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

5
"सरल और अधिक सहज": क्षमा करें, लेकिन मैं नहीं देखता कि कैसे। "अद्यतन करना। तर्क [0]" सार्थक नहीं है , मेरे पास एक नामित तर्क है और संकलक को स्वयं जांचने दें कि क्या तर्कों की संख्या मेल खाती है: pastebin.com/YtUf1FpG
coredump

8
"एक प्रदर्शन के दृष्टिकोण से, यह बहुत सरल कार्य है; कई उच्च-स्तरीय भाषा संकलक 75% या अधिक समय पार्सिंग पर खर्च करते हैं।" मैं उम्मीद कर रहा हूँ कि अधिकांश समय लेने के लिए अनुकूलन की तलाश करूँ (लेकिन मैंने कभी वास्तविक संकलक नहीं लिखा है )। क्या मुझसे कोई चूक हो रही है?
डोभाल

5
दुर्भाग्य से आपका उदाहरण यह नहीं दिखाता है। यह मैक्रोज़ के साथ किसी भी लिस्प में लागू करने के लिए आदिम है। वास्तव में यह लागू करने के लिए सबसे आदिम मैक्रोज़ में से एक है। इससे मुझे संदेह है कि आपको लिस्प में मैक्रों के बारे में ज्यादा जानकारी नहीं है। "लिस्प का सिंटैक्स 1960 के दशक में अटक गया है": वास्तव में लिस्प में मैक्रो सिस्टम ने 1960 के बाद से बहुत प्रगति की है (1960 में लिस्प में मैक्रोज़ भी नहीं थे!)।
रेनर जोसविग

3

मैं SICP से स्कीम सीख रहा हूं और मुझे आभास हो रहा है कि स्कीम का एक बड़ा हिस्सा और इससे भी ज्यादा, LISP विशेष मैक्रो सिस्टम है।

ऐसा कैसे? SICP में सभी कोड मैक्रो-फ्री स्टाइल में लिखे गए हैं। SICP में कोई मैक्रोज़ नहीं हैं। केवल 373 पृष्ठ पर एक फुटनोट में कभी भी मैक्रोज़ का उल्लेख किया गया है।

लेकिन, चूंकि मैक्रोज़ का संकलन-समय पर विस्तार किया जाता है

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

चलो परीक्षण करते हैं कि एसबीसीएल का उपयोग करते हुए, एक सामान्य लिस्प कार्यान्वयन।

एसबीसीएल को दुभाषिया में बदल दें:

* (setf sb-ext:*evaluator-mode* :interpret)

:INTERPRET

अब हम एक मैक्रो को परिभाषित करते हैं। मैक्रो कुछ को प्रिंट करता है जब इसे कोड विस्तारित के लिए कहा जाता है। उत्पन्न कोड प्रिंट नहीं करता है।

* (defmacro my-and (a b)
    (print "macro my-and used")
    `(if ,a
         (if ,b t nil)
         nil))

अब मैक्रो का उपयोग करते हैं:

MY-AND
* (defun foo (a b) (my-and a b))

FOO

देख। उपरोक्त मामले में लिस्प कुछ नहीं करता है। परिभाषा समय पर मैक्रो का विस्तार नहीं किया गया है।

* (foo t nil)

"macro my-and used"
NIL

लेकिन रनटाइम पर, जब कोड का उपयोग किया जाता है, तो मैक्रो का विस्तार किया जाता है।

* (foo t t)

"macro my-and used"
T

फिर, रनटाइम पर, जब कोड का उपयोग किया जाता है, तो मैक्रो का विस्तार किया जाता है।

ध्यान दें कि संकलक का उपयोग करते समय SBCL केवल एक बार विस्तार करेगा। लेकिन विभिन्न लिस्प कार्यान्वयन भी दुभाषियों को प्रदान करते हैं - जैसे एसबीसीएल।

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


मैं वेब पर SICP पढ़ने के साथ-साथ योजना के बारे में बहुत कुछ पढ़ रहा हूं। इसके अलावा, व्याख्या करने से पहले लिस्प अभिव्यक्ति संकलित नहीं की जाती है? उन्हें कम से कम पार्स किया जाना चाहिए। इसलिए मुझे लगता है कि "संकलन-समय" को "पार्स-टाइम" होना चाहिए।
इलियट गोरोखोवस्की

@ RenéG Rainer की बात, मेरा मानना ​​है कि, यदि आप evalया loadकिसी लिस्प भाषा में कोड, उन में मैक्रोज़ संसाधित हो जाएगा। जबकि अगर आप अपने प्रश्न में प्रस्तावित प्रीप्रोसेसर प्रणाली का उपयोग करते हैं, evalऔर जैसे मैक्रो विस्तार से लाभ नहीं होगा।
क्रिस जस्टर-यंग

@ RenéG इसके अलावा, "पार्स" को readलिस्प में कहा जाता है। यह अंतर महत्वपूर्ण है, क्योंकि evalसूची डेटा संरचनाओं पर वास्तविक काम करता है (जैसा कि मेरे उत्तर में उल्लेख किया गया है), पाठ रूप पर नहीं। तो आप उपयोग कर सकते हैं (eval '(+ 1 1))और वापस 2 प्राप्त कर सकते हैं , लेकिन यदि आप (eval "(+ 1 1)"), आप वापस "(+ 1 1)"(स्ट्रिंग) प्राप्त करते हैं। आप (7 अक्षरों की एक स्ट्रिंग) readसे प्राप्त करने के लिए उपयोग करते हैं "(+ 1 1)"( (+ 1 1)एक प्रतीक और दो फ़िक्नेम्स के साथ एक सूची)।
क्रिस जैस्टर-यंग

@ RenéG उस समझ के साथ, मैक्रोज़ readसमय पर काम नहीं करते हैं । वे इस अर्थ में संकलन-समय पर काम करते हैं कि यदि आपको कोड पसंद है (and (test1) (test2)), तो यह (if (test1) (test2) #f)कोड में लोड होने पर, केवल एक बार कोड में लोड होने के बाद (स्कीम में) विस्तारित हो जाएगा , लेकिन यदि आप कुछ ऐसा करते हैं (eval '(and (test1) (test2))), कि संकलन (और मैक्रो-विस्तार) कि अभिव्यक्ति उचित रूप से, रनटाइम पर होगा।
क्रिस जस्टर-यंग

@ RenéG Homoiconicity वह है जो लिस्प भाषाओं को शाब्दिक रूप के बजाय सूची संरचनाओं पर विकसित करने की अनुमति देता है, और निष्पादन से पहले उन सूची संरचनाओं को (मैक्रोज़ के माध्यम से) रूपांतरित करने की अनुमति देता है। अधिकांश भाषाएं evalकेवल पाठ के तार पर काम करती हैं, और वाक्यविन्यास संशोधन के लिए उनकी क्षमताओं में बहुत अधिक कमी और / या बोझिल हैं।
क्रिस जस्टर-यंग

1

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

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

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

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