2008-09-15 15 views
6

मैं एक लिस्प मैक्रो लिखने का प्रयास कर रहा हूं जो अर्थपूर्ण कारणों से अन्य प्रोग्रामिंग भाषाओं में ++ के बराबर होगा। मैंने इसे कई अलग-अलग तरीकों से करने का प्रयास किया है, लेकिन उनमें से कोई भी काम नहीं कर रहा है, और सभी को दुभाषिया द्वारा स्वीकार किया जाता है, इसलिए मुझे नहीं पता कि मेरे पास सही वाक्यविन्यास है या नहीं। यह कैसे परिभाषित किए जाएँगे का मेरा विचारसामान्य लिस्प में एक ++ मैक्रो लिखना

(defmacro ++ (variable) 
    (incf variable)) 

होगा, लेकिन जब इसका इस्तेमाल करने की कोशिश कर रहा यह मुझे एक सरल-TYPE-त्रुटि देता है। यह क्या काम करेगा?

+0

डुप्लिकेट नहीं है, लेकिन संबंधित: [? INCF की तरह एक विनाशकारी मैक्रो या समारोह लेखन] (http://stackoverflow.com/q/19485964/1281433) –

उत्तर

17

याद रखें कि एक मैक्रो मूल्यांकन करने के लिए एक अभिव्यक्ति देता है। चाल करना चाहिए

(defmacro ++ (variable) 
    `(incf ,variable)) 
-2

हालांकि मैं एक तुतलाना गुरु नहीं कर रहा हूँ: ऐसा करने के लिए है, तो आप backquote किया है।

(defmacro ++ (variable) 
    `(setq ,variable (+ ,variable 1))) 
+4

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

+0

यह भी काम नहीं करता है अगर 'चर' एक चर के अलावा कुछ भी है (या प्रतीक मैक्रो), क्योंकि 'setq' गैर-चर के साथ काम नहीं करता है। उदाहरण के लिए, आप '(++ (कार सूची) नहीं कर सकते हैं '। –

12
पिछले जवाब की

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

(++ varname) 
VARNAME ++ या ++ VARNAME के ​​बजाय

है, जो मुझे लगता है आप चाहते हैं। मुझे नहीं पता कि क्या आप वास्तव में पूर्व प्राप्त कर सकते हैं, लेकिन बाद के लिए, आप एक पढ़ा मैक्रो कर सकते हैं। चूंकि यह दो वर्ण हैं, इसलिए प्रेषण मैक्रो शायद सबसे अच्छा है। Untested, के बाद से मैं एक आसान चल तुतलाना, लेकिन की तरह कुछ भी नहीं है:

(defun plusplus-reader (stream subchar arg) 
    (declare (ignore subchar arg)) 
    (list 'incf (read stream t nil t))) 
(set-dispatch-macro-character #\+ #\+ #'plusplus-reader) 

बनाना चाहिए ++ वर वास्तव में (INCF वर) के रूप में पढ़ा।

4

पूर्व वेतन वृद्धि के लिए, वहाँ पहले से ही INCF है, लेकिन आप

(define-modify-macro my-incf() 1+) 

बाद वेतन वृद्धि के लिए के साथ अपने स्वयं परिभाषित कर सकते हैं, तो आप इस्तेमाल कर सकते हैं इस (किराया-utils से):

(defmacro define-values-post-modify-macro (name val-vars lambda-list function) 
"Multiple-values variant on define-modify macro, to yield pre-modification values" 
(let ((env (gensym "ENV"))) 
    `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env) 
     (multiple-value-bind (vars vals store-vars writer-form reader-form) 
      (get-setf-expansion `(values ,,@val-vars) ,env) 
     (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp))) 
           ',val-vars))) 
      `(let* (,@(mapcar #'list vars vals) 
        ,@store-vars) 
      (multiple-value-bind ,val-temps ,reader-form 
       (multiple-value-setq ,store-vars 
       (,',function ,@val-temps ,,@lambda-list)) 
       ,writer-form 
       (values ,@val-temps)))))))) 

(defmacro define-post-modify-macro (name lambda-list function) 
"Variant on define-modify-macro, to yield pre-modification values" 
`(define-values-post-modify-macro ,name (,(gensym)) ,lambda-list ,function)) 

(define-post-modify-macro post-incf() 1+) 
7

अर्थात्, उपसर्ग ऑपरेटर ++ और - सी ++ जैसी भाषा में या सामान्य लिस्प में जो भी समकक्ष incf/decf हैं। यदि आप इसे महसूस करते हैं और, आपके (गलत) मैक्रो की तरह, वास्तव में एक वाक्य रचनात्मक परिवर्तन की तलाश में हैं तो आप पहले ही दिखाए जा चुके हैं कि इसे '(incf, x) जैसे बैकटिक्स के साथ कैसे किया जाए। आपको यह भी दिखाया गया है कि गैर-लिस्प वाक्यविन्यास के करीब कुछ पाने के लिए पाठक को इस बारे में कैसे हैक करना है। हालांकि यह रगड़ है, क्योंकि इन चीजों में से कोई भी एक अच्छा विचार नहीं है। आम तौर पर, एक भाषा बनाने के लिए गैर idiomatic कोडिंग एक और अधिक बारीकी से मिलती है बस इतना अच्छा विचार नहीं निकलता है।

हालांकि, अगर वास्तव में अर्थशास्त्र की तलाश में हैं, तो आपको पहले से ही उपसर्ग संस्करणों को नोट किया गया है लेकिन पोस्टफिक्स संस्करणों को वाक्य रचनात्मक रूप से मिलान करना आसान नहीं होगा। आप इसे पर्याप्त पाठक हैकर के साथ कर सकते हैं, लेकिन यह सुंदर नहीं होगा।

यदि आप यही खोज रहे हैं, तो मैं सुझाव दूंगा कि) एसीएफ/डीसीएफ नामों के साथ चिपके रहें क्योंकि वे मूर्ख हैं और अच्छी तरह से काम करते हैं और बी) पोस्ट-इंक, पोस्ट-डीसीएफ संस्करण, जैसे (डिफैमक्रो पोस्ट- INCF (एक्स) `(prog1, एक्स (INCF, x)) चीज़ों के प्रकारों।

व्यक्तिगत रूप से, मैं नहीं दिख रहा है कि यह कैसे विशेष रूप से उपयोगी हो सकता है लेकिन YMMV होगा।

+1

'prog1' का उल्लेख स्वयं ही इस पोस्ट के लिए पर्याप्त कारण है। लंबे समय तक सीएल का उपयोग कर रहे थे, और भूल गए थे कि यह बहुत समय पहले अस्तित्व में था। – Mars

8

मैं दृढ़ता से एक बनाने के खिलाफ सलाह देंगे incf के लिए उपनाम। यह आपके कोड को पढ़ने वाले किसी और के लिए पठनीयता को कम करेगा, जिसे खुद से पूछना है "यह क्या है? यह incf से अलग कैसे है?"

आप एक सरल बाद वेतन वृद्धि चाहते हैं, इस प्रयास करें:

(defmacro post-inc (number &optional (delta 1)) 
    "Returns the current value of number, and afterwards increases it by delta (default 1)." 
    (let ((value (gensym))) 
    `(let ((,value ,number)) 
     (incf ,number ,delta) 
     ,value))) 
+1

हालांकि यह दो बार 'संख्या' का मूल्यांकन करता है। [काज़ का जवाब] (http://stackoverflow.com/a/10567794/1281433) दिखाता है कि इससे कैसे बचें। –

8

वाक्य रचना (++ a)(incf a) के लिए एक बेकार उर्फ ​​है लेकिन आप के बाद वेतन वृद्धि के शब्दों चाहते लगता है:। वर्ष मान प्राप्त। सामान्य लिस्प में, यह prog1 के साथ किया जाता है, जैसा कि: (prog1 i (incf i))। सामान्य लिस्प अविश्वसनीय या संदिग्ध मूल्यांकन आदेश से ग्रस्त नहीं है। पिछली अभिव्यक्ति का अर्थ है कि i का मूल्यांकन किया गया है, और मूल्य कहीं भी छीन लिया गया है, तो (incf i) का मूल्यांकन किया गया है, और तो स्टैश किए गए मान को वापस कर दिया जाता है।

पूरी तरह से बुलेट प्रूफ बनाना pincf (पोस्ट -incf) पूरी तरह से तुच्छ नहीं है। (incf i) में अच्छी संपत्ति है कि i का मूल्यांकन केवल एक बार किया जाता है। हम (pincf i) भी उस संपत्ति के लिए चाहते हैं। और इसलिए सरल मैक्रो कम पड़ता है:

(defmacro pincf (place &optional (increment 1)) 
    `(prog1 ,place (incf ,place ,increment)) 

यह अधिकार हम लिस्प के "काम जगह विश्लेषक" सामग्री है कि अनुमति देने के प्राप्त करने के लिए कहा जाता है get-setf-expansion का सहारा लेना है ऐसा करने के लिए हमारी पहुंच को ठीक से संकलित करने के लिए मैक्रो:

(defmacro pincf (place-expression &optional (increment 1) &environment env) 
    (multiple-value-bind (temp-syms val-forms 
         store-vars store-form access-form) 
         (get-setf-expansion place-expression env) 
    (when (cdr store-vars) 
     (error "pincf: sorry, cannot increment multiple-value place. extend me!")) 
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms) 
     (let ((,(car store-vars) ,access-form)) 
     (prog1 ,(car store-vars) 
       (incf ,(car store-vars) ,increment) 
       ,store-form))))) 

सीएलआईएसपी के साथ कुछ परीक्षण। (नोट: get-setf-expansion से सामग्री पर निर्भर विस्तार कार्यान्वयन-विशिष्ट कोड शामिल हो सकता है इसका मतलब यह नहीं हमारी मैक्रो पोर्टेबल नहीं है।!)

8]> (macroexpand `(pincf simple)) 
(LET* ((#:VALUES-12672 (MULTIPLE-VALUE-LIST (VALUES)))) 
(LET ((#:NEW-12671 SIMPLE)) 
    (PROG1 #:NEW-12671 (INCF #:NEW-12671 1) (SETQ SIMPLE #:NEW-12671)))) ; 
T 
[9]> (macroexpand `(pincf (fifth list))) 
(LET* 
((#:VALUES-12675 (MULTIPLE-VALUE-LIST (VALUES LIST))) 
    (#:G12673 (POP #:VALUES-12675))) 
(LET ((#:G12674 (FIFTH #:G12673))) 
    (PROG1 #:G12674 (INCF #:G12674 1) 
    (SYSTEM::%RPLACA (CDDDDR #:G12673) #:G12674)))) ; 
T 
[10]> (macroexpand `(pincf (aref a 42))) 
(LET* 
((#:VALUES-12679 (MULTIPLE-VALUE-LIST (VALUES A 42))) 
    (#:G12676 (POP #:VALUES-12679)) (#:G12677 (POP #:VALUES-12679))) 
(LET ((#:G12678 (AREF #:G12676 #:G12677))) 
    (PROG1 #:G12678 (INCF #:G12678 1) 
    (SYSTEM::STORE #:G12676 #:G12677 #:G12678)))) ; 
T 

अब यहाँ एक महत्वपूर्ण परीक्षण का मामला है। यहां, स्थान में एक साइड इफेक्ट है: (aref a (incf i))। यह एक बार मूल्यांकन किया जाना चाहिए!

[11]> (macroexpand `(pincf (aref a (incf i)))) 
(LET* 
((#:VALUES-12683 (MULTIPLE-VALUE-LIST (VALUES A (INCF I)))) 
    (#:G12680 (POP #:VALUES-12683)) (#:G12681 (POP #:VALUES-12683))) 
(LET ((#:G12682 (AREF #:G12680 #:G12681))) 
    (PROG1 #:G12682 (INCF #:G12682 1) 
    (SYSTEM::STORE #:G12680 #:G12681 #:G12682)))) ; 
T 

तो क्या पहले ऐसा होता है कि A और (INCF I) मूल्यांकन किया जाता है है, और अस्थायी चर #:G12680 और #:G12681 हो जाते हैं। सरणी का उपयोग किया जाता है और मान #:G12682 में कैप्चर किया जाता है। फिर हमारे पास PROG1 है जो कि उस मूल्य को वापस करने के लिए बनाए रखता है। मान बढ़ाया गया है, और सीएलआईएसपी के system::store फ़ंक्शन के माध्यम से सरणी स्थान पर वापस संग्रहीत किया गया है। ध्यान दें कि यह स्टोर कॉल अस्थायी चर का उपयोग करता है, मूल अभिव्यक्ति A और I नहीं। (INCF I) केवल एक बार प्रकट होता है।

+1

@ जोशुआटाइलर 'परिभाषित-संशोधित-मैक्रो' द्वारा निर्मित एक मैक्रो नया, अद्यतन मान देता है। चूंकि यही है 'incf' को वापस करने की आवश्यकता है, यह आसान है। यह स्पष्ट नहीं है कि 'डिफिन-संशोधित-मैक्रो' के साथ 'पंकफ' लिखना उतना ही आसान है, जहां आवश्यकता उस स्थान को वापस करना है जो पहले स्थान पर थी। – Kaz

1

हालांकि मैं निश्चित रूप से टिप्पणी और ध्यान में रखना होगा सिर-अप अपनी पोस्ट में साइमन टिप्पणियाँ, मैं सच में लगता है कि कि user10029 के दृष्टिकोण अभी भी लायक एक कोशिश है, इसलिए, सिर्फ मनोरंजन के लिए, मैं करने की कोशिश की ++ x ऑपरेटर कार्य (यानी, x में x के मान में वृद्धि) बनाने के स्वीकार्य उत्तर के साथ इसे गठबंधन करने के लिए। कोशिश करो!

स्पष्टीकरण: अच्छा पुराने SBCL अपने संस्करण को संकलित नहीं करेंगे क्योंकि '+' प्रतीक स्पष्ट make-dispatch-macro-character साथ प्रेषण-चार लुकअप तालिका पर सेट किया जाना चाहिए, और मैक्रो अभी भी जरूरत है के नाम पर पारित करने के इसका मूल्यांकन करने से पहले परिवर्तनीय। एक उपयोग उदाहरण के लिए

(defmacro increment (variable) 
    "The accepted answer" 
    `(incf ,variable)) 

(make-dispatch-macro-character #\+) ; make the dispatcher grab '+' 

(defun |inc-reader| (stream subchar arg) 
    "sets ++<NUM> as an alias for (incf <NUM>). 
    Example: (setf x 1233.56) =>1233.56 
      ++x => 1234.56 
      x => 1234.56" 
    (declare (ignore subchar arg)) 
    (list 'increment (read stream t nil t))) 

(set-dispatch-macro-character #\+ #\+ #'|inc-reader|) 

|inc-reader| के देखें docstring: तो यह काम करना चाहिए।(निकट) संबंधित दस्तावेज यहां पाया जा सकता:

इस कार्यान्वयन परिणाम के रूप में 123 की तरह है कि संख्या में प्रविष्टियों नहीं रह गया है समझ रहे हैं है (डिबगर no dispatch function defined for #\Newline साथ में कूदता) लेकिन आगे कामकाज (या यहां तक ​​कि से बचने) उचित लगता है: यदि आप अभी भी इसके साथ रहना चाहते हैं, तो शायद सबसे अच्छा विकल्प ++ उपसर्ग के रूप में नहीं लेना है, लेकिन ## या किसी अन्य डीएसएल-आईएसएच समाधान

चीयर्स!

एन्ड्रेस

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