लिस्प मैक्रोज़ कितने उपयोगी हैं?


22

आम लिस्प आपको मैक्रोज़ लिखने की अनुमति देता है जो आप चाहते हैं कि जो भी स्रोत परिवर्तन हो।

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

Viaweb संपादक का स्रोत कोड संभवतः लगभग 20-25% मैक्रोज़ था।

लोग वास्तव में मैक्रों के साथ किस तरह की बातें करते हैं?


मुझे लगता है कि यह निश्चित रूप से अच्छे व्यक्तिपरक है , मैंने आपके प्रश्न को प्रारूपण के लिए संपादित किया है। यह एक डुप्लिकेट हो सकता है, लेकिन मुझे एक नहीं मिला।
टिम पोस्ट

1
सब कुछ दोहराए जाने योग्य नहीं है जो एक समारोह में फिट होता है, मुझे लगता है।

2
आप किसी भी वाक्य रचना और किसी भी शब्दार्थ के साथ किसी भी अन्य भाषा में लिस्प को चालू करने के लिए मैक्रोज़ का उपयोग कर सकते हैं : bit.ly/vqqvHU
SK-logic

programmers.stackexchange.com/questions/81202/… यहाँ देखने लायक है, लेकिन यह डुप्लिकेट नहीं है।
डेविड थॉर्नले

जवाबों:


15

2002 में एलएल 1 चर्चा सूची में मथियास फेलेसेन द्वारा इस पोस्ट पर एक नज़र डालें । उन्होंने मैक्रों के लिए तीन मुख्य उपयोग सुझाए:

  1. डेटा उप-भाषाएं : मैं सरल-दिखने वाले भाव लिख सकता हूं और जटिल नेस्टेड सूचियाँ / सरणियाँ / सारणियाँ बना सकता हूँ, जिन्हें मैक्रो के साथ बड़े करीने से तैयार किया गया है।
  2. बाइंडिंग कंस्ट्रक्शन : मैं मैक्रोज़ के साथ नए बाइंडिंग कंस्ट्रक्शन पेश कर सकता हूं। इससे मुझे लंबोदर से छुटकारा पाने और चीजों को एक साथ रखने में मदद मिलती है।
  3. मूल्यांकन पुनर्मूल्यांकन : मैं उन निर्माणों को प्रस्तुत कर सकता हूं जो आवश्यकतानुसार देरी से अभिव्यक्ति के मूल्यांकन को स्थगित / स्थगित करते हैं। छोरों, नई स्थितियों, विलंब / बल आदि के बारे में सोचें [नोट: हास्केल या किसी भी आलसी भाषा में, यह अनावश्यक है।]

18

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

उदाहरण के लिए, मैंने हाल ही में खुद for-loopको C ++ / Java के समान अनिवार्यता पाया । हालाँकि, एक कार्यात्मक भाषा होने के नाते, क्लोजर बॉक्स से एक के साथ नहीं आया था। इसलिए मैंने इसे एक मैक्रो के रूप में लागू किया:

(defmacro for-loop [[sym init check change :as params] & steps]
  `(loop [~sym ~init value# nil]
     (if ~check
       (let [new-value# (do ~@steps)]
         (recur ~change new-value#))
       value#)))

और अब मैं कर सकता हूँ:

 (for-loop [i 0 , (< i 10) , (inc i)] 
   (println i))

और आपके पास यह है - कोड की छह लाइनों में एक नया सामान्य-उद्देश्य संकलन-समय भाषा का निर्माण।


13

लोग वास्तव में मैक्रों के साथ किस तरह की बातें करते हैं?

भाषा एक्सटेंशन या डीएसएल लिखना।

लिस्प जैसी भाषाओं में इसके लिए एक महसूस करने के लिए, रैकेट का अध्ययन करें , जिसमें कई भाषा संस्करण हैं: टाइपेड रैकेट, R6RS और Datalog।

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


4

यहाँ कुछ उदाहरण हैं:

योजना:

  • defineफ़ंक्शन परिभाषाओं के लिए। मूल रूप से यह एक फ़ंक्शन को परिभाषित करने का एक छोटा तरीका है।
  • let lexically scoped चर बनाने के लिए।

Clojure:

  • defn, इसके डॉक्स के अनुसार:

    (जैसे नाम (fn [params *] exprs *)) या (def name (fn ([params *] exprs *) +) +) +)) किसी भी डॉक्टर-स्ट्रिंग या attrs के साथ var मेटाडेटा में जोड़ा जाता है

  • for: सूची बोध
  • defmacro: विडंबना?
  • defmethod, defmulti: बहु तरीकों के साथ काम
  • ns

इनमें से बहुत सारे मैक्रोज़ को अधिक सार स्तर पर कोड लिखना बहुत आसान हो जाता है। मुझे लगता है कि मैक्रों समान हैं, कई मायनों में, गैर-लिस्प में वाक्य रचना के लिए।

साजिश रचने वाली लाइब्रेरी इंकेटर कुछ जटिल डेटा जोड़तोड़ के लिए मैक्रोज़ प्रदान करता है।


4

मैक्रोज़ कुछ पैटर्न एम्बेडेड करने के लिए उपयोगी होते हैं।

उदाहरण के लिए, कॉमन लिस्प whileलूप को परिभाषित नहीं करता है लेकिन इसका do इस्तेमाल इसे परिभाषित करने के लिए किया जा सकता है।

यहां ऑन लिस्प का एक उदाहरण दिया गया है ।

(defmacro while (test &body body)
  `(do ()
       ((not ,test))
     ,@body))

(let ((a 0))
  (while (< a 10)
    (princ (incf a))))

यह "12345678910" प्रिंट करेगा, और यदि आप यह देखने की कोशिश करेंगे कि क्या होता है macroexpand-1:

(macroexpand-1 '(while (< a 10) (princ (incf a))))

यह लौटेगा:

(DO () ((NOT (< A 10))) (PRINC (INCF A)))

यह एक सरल मैक्रो है, लेकिन जैसा कि पहले कहा गया था, वे आमतौर पर नई भाषाओं या डीएसएल को परिभाषित करने के लिए उपयोग किए जाते हैं, लेकिन इस सरल उदाहरण से आप पहले से ही कल्पना कर सकते हैं कि आप उनके साथ क्या कर सकते हैं।

loopमैक्रो मैक्रो क्या कर सकते हैं का एक अच्छा उदाहरण है।

(loop for i from 0 to 10
      if (and (= (mod i 2) 0) i)
        collect it)
=> (0 2 4 6 8 10)
(loop for i downfrom 10 to 0
      with a = 2
      collect (* a i))
=> (20 18 16 14 12 10 8 6 4 2 0)               

आम लिस्प में एक और तरह का मैक्रोज़ होता है जिसे रीडर मैक्रो कहा जाता है, जिसका इस्तेमाल यह समझने के लिए किया जा सकता है कि पाठक कोड की व्याख्या कैसे करता है, अर्थात आप उनका उपयोग करने के लिए # {और #} का उपयोग कर सकते हैं जैसे # (और #)।


3

यहाँ एक मैं डिबगिंग के लिए उपयोग कर रहा हूँ (क्लोजर में):

user=> (defmacro print-var [varname] `(println ~(name varname) "=" ~varname))
#'user/print-var
=> (def x (reduce * [1 2 3 4 5]))
#'user/x
=> (print-var x)
x = 120
nil

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

#define LET(name, value, body)  \
    do {                        \
        string name(value);     \
        body;                   \
        assert(name == value);  \
    } while (false)

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

मैं सामान को लपेटने की भयानक बदसूरत हैक का भी सहारा do ... while (false)लेता हूं ताकि आप इसे इफ़े के हिस्से में उपयोग कर सकें और अभी भी उम्मीद के मुताबिक अन्य भाग काम कर सकें। आपको लिस्प में इसकी आवश्यकता नहीं है, जो स्ट्रिंग्स (या टोकन अनुक्रमों के बजाय, सी और सी ++ के मामले में) के बजाय सिंटैक्स पेड़ों पर काम करने वाले मैक्रोज़ का एक कार्य है, जो तब पार्सिंग से गुजरता है।

कुछ अंतर्निहित थ्रेडिंग मैक्रोज़ हैं जिनका उपयोग आपके कोड को पुनर्गठित करने के लिए किया जा सकता है जैसे कि यह अधिक सफाई से पढ़ता है ('थ्रेडिंग' जैसे 'अपने कोड को एक साथ बोना', समानता नहीं)। उदाहरण के लिए:

(->> (range 6) (filter even?) (map inc) (reduce *))

यह पहला रूप लेता है (range 6), और इसे अगले रूप का अंतिम तर्क बनाता है (filter even?), जो बदले में अगले रूप का अंतिम तर्क बनाता है और इसी तरह, जैसे कि ऊपर फिर से लिखा जाता है।

(reduce * (map inc (filter even? (range 6))))

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

एक संस्करण भी है जो पिछले फॉर्म को पहले (अंतिम की बजाय) तर्क के रूप में सम्मिलित करता है। एक उपयोग का मामला अंकगणित है:

(-> 17 (- 2) (/ 3))

"17 लेने, 2 घटाना और 3 से विभाजित" के रूप में पढ़ता है।

अंकगणित की बात करें, तो आप एक मैक्रो लिख सकते हैं, जो इन्फिक्स नोटेशन पार्सिंग करता है, ताकि आप उदाहरण के लिए कह सकें (infix (17 - 2) / 3)और यह थूक देगा, (/ (- 17 2) 3)जिसमें कम पठनीय होने का नुकसान है और एक वैध लिस्प अभिव्यक्ति होने का फायदा है। यह डीएसएल / डेटा सबलंगेज हिस्सा है।


1
फंक्शन एप्लीकेशन मेरे लिए थ्रेडिंग की तुलना में बहुत अधिक मायने रखता है, लेकिन यह निश्चित रूप से आदत की बात है। अच्छा जवाब।
coredump
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.